第1回 File クラス - ファイル操作のテクニック - PEAR講座

PHP基礎編

くまっち先生のPEAR講座

Lecutures on PHP

第1回 File クラス - ファイル操作のテクニック (その1)

 このエントリーをはてなブックマークに追加

PEAR ライブラリを読もう

くまっち先生PEAR は、PHP 上で利用できるオープンソースなライブラリ集です。用途にあわせた数々のライブラリが公開されており、世界中で幅広く利用されています。ライブラリそのものは PHP のコードで書かれているため、パッケージをダウンロードしエディタでファイルを開くことで簡単にその内容を見ることができます。

世界中で使われるライブラリは果たしてどのように作られているのでしょうか。この講座では、PEAR で公開されているライブラリのソースコードを見ながら、目的の処理がどうプログラミングされているのかを読んでいきたいと思います。

第一回目は、ファイルに対する処理を行う File パッケージとして提供されている File クラスの中身を覗いてみることにしましょう。

プログラムコードを書いたことがある方は、ファイルに対してのデータ操作を行うケースにあうことは少なくないのではないでしょうか。File クラスはよくあるファイルのデータ操作を簡単に行うため機能を持っています。皆さんならファイルのデータ操作プログラムをどのように書きますか?

ファイルの内容を読み込む機能

でははじめにファイルの内容を読み込む機能である、指定したファイルからある容量分の内容を読み込む read メソッドをみてみることにしましょう。くまっち先生

指定するファイルの内容を読み込む処理を実際にプログラムのコードへ起こす際、どのようなことを考えなければならないでしょう?ファイル処理を行う PHP 関数を使用し、ファイルハンドラを作成するとともにデータを読み込む処理をコードにするのはもちろんのこと、そもそも指定されたファイルが実際に存在しているのかというような前提条件の確認を行う必要もあります。仮に存在しないファイルを指定された場合は、その旨を示さなければなりません。

実際に File クラスの read メソッドでどのようにしてこれらを実現しているのでしょうか。以下のリスト1がそのコードです。(一部コメントにて注釈しています。)

リスト1 read メソッドの内容

function read($filename$size FILE_DEFAULT_READSIZE$lock false)
{
  static 
$filePointers;
        
  
// 1
  
if (== $size) {
    return 
File::readAll($filename$lock);
  } 

  
// 2
  
if (!isset($filePointers[$filename]) || 
    !
is_resource($filePointers[$filename])) {
    if (
PEAR::isError($fp = &File::_getFilePointer($filename
      
FILE_MODE_READ$lock))) {
      return 
$fp;
    } 

    
$filePointers[$filename] = &$fp;
    } else {
      
$fp = &$filePointers[$filename];
    } 

    
// 3
    
return !feof($fp) ? fread($fp$size) : false;
}

まず、この read メソッドでは以下のような引数を準備しています。

  • $filename - 読み込むファイル名を指定
  • $size - 読み込む容量(バイト)を指定 (指定しなかった場合は FILE_DEFAULT_READSIZE を使用)
  • $lock - ファイルロックの種類を指定(指定しなかった場合は使用しない)

$size, $lock に関しては、メソッド自身が初期値を準備していますので、利用者は最小限の準備としてファイル名の指定だけで実行可能なことがわかります。思い出してみてください。プログラムに限らず私たちがパソコンを利用する上で、あるファイルの中身を参照したい場合はそのファイル名を指定して「開く」とします。

次にこのコードの処理ステップを見てみましょう。順を追ってみていくと、以下のような処理を行っていることがわかります。

    くまっち先生
  • (1) 読み込むファイル容量の指定が 0 ならば、readAll メソッドを実行し、その結果を返す
  • (2) 指定したファイルのファイルポインタ情報が残っているか?
    • -> a. 残っていなければ、ファイルポインタ情報を取得し、その情報を保存する
    • -> b. 残っていれば、そのファイルポインタ情報を再利用する
  • (3) ファイルポインタが、ファイルの終端に達しているか?
    • -> a. 達していなければ、指定バイト数のデータを読み込み、その内容を返す
    • -> b. 達していれば、false を返す

(1) では、読み込む容量の指定が 0 である場合に「全てのデータを読み込む」という仕様にしているようです。ファイルの内容を全て読み込む機能として別途準備された File クラスの readAll メソッドを実行しています。return していますので、以後の処理は行われません。

(2) では、指定するファイルのファイルポインタ情報が残っているかどうかにより処理の分岐をしています。
「ファイルポインタ情報が残っている」とはどういうことでしょう?read メソッドは指定する容量分のデータを読み込む処理を行いますので、実際にこのメソッドの機能を利用するプログラマは「メソッドの連続使用」を行うのではないかと想像することができます。たとえば、以下のリスト 2 のようなコードです。

リスト2 read メソッドの使用例

require_once('File.php');
$filename 'example.txt';
while (
$data File::read($filename)) {
  
// 読み込んだデータを出力する
  
echo $data;
}

このように連続してファイルからデータを読み込む場合、 read メソッド内で読み込んだデータ分だけファイルポインタを進めておかなければ、常に同じデータを返すことになってしまいます。そのようなことがないようにする簡単な解決方法として、使用したファイルポインタ情報をどこかに残しておけばよいでしょう。くまっち先生

ほかにも「どれだけデータを読み込んだか」として容量情報を残しておく方法もありますが、連続して読み込む場合「その容量分ファイルポインタを進める」というような処理を別途準備せねばならないため現実的ではありません。本来の目的を忘れない、つまり「ファイルからデータを読み込むコードを準備する」ことから外れすぎないことも、プログラムを書く上で重要なポイントです。

さて、指定ファイルからはじめてデータを読み込む場合は当然ながらファイルポインタ情報はありません。条件分岐にて情報がなかった場合に同 File クラス内の _getFilePointer メソッドを実行し、$fp という変数として受け取っています。

情報がないため実行するメソッド……どうやらこのメソッドがファイルポインタ情報を作成する役割を果たしているようです。この _getFilePointer メソッド内でどのような事が行われているかはひとまず気にせず、「ファイルポインタ情報が帰ってくる」と仮定して続きをみていくことにします。

取得した情報を、PEAR::isError というクラスメソッドの引数に利用し条件分岐の確認を行っています。PEAR::isError は、その値が PEAR エラーオブジェクトかどうかを確認する機能です。この条件分岐が true である、つまり $fp がエラーオブジェクトの場合は、その内容をそのまま返しています。

このように、メソッド内でなんらかの問題が発生した場合に的確な例外処理を発生させることも、プログラムを書く上では重要になってきます。ライブラリ作成者はエラーを知らせ、ライブラリ利用者はそのエラーを知るということです。

_getFilePointer 内で何かしら問題が発生した場合は PEAR エラーオブジェクトを返すことで知らせるようにしています。またそのエラーオブジェクトをそのまま read メソッドの返り値として使用することで、利用者へも問題を知らせる仕様にしているようです。したがって先ほど使用例として示したリスト 2 のコードでは不十分で、以下のリスト 3 のようなコードを書く必要があるでしょう。

リスト3 read メソッドの使用例(修正版)

require_once('File.php');
$filename 'example.txt';
while (
$data File::read($filename)) {
  if (
PEAR::isError($data)) {
    echo 
"エラーが発生しました。({$data->getMessage()})";
    exit;
  }

  
// 読み込んだデータを出力する
  
echo $data;
}

取得した情報に問題がなかった場合、$filePointers という静的変数へリファレンス(参照)として保存しています。静的変数は、そのメソッドの実行が完了した後もその値を失いません。したがってリスト 2 のコードのように連続して同じクラスメソッドを実行するようなケースでは、静的変数に保存したファイルポインタ情報を保持したまま処理を続けることができます。

またリファレンスとして保存していますので、ファイルポインタの情報に変更があった場合でも追随して値を参照することができます。(*1) このように、今回のような「ファイルポインタ情報」という同一のものを参照した場合は、変数値はコピーで取り扱うのではなくリファレンスで取り扱うよう心がけることがトラブルの防止につながります。

(3) でようやく本来の目的であるデータの読み込み処理を行っています。ただし、ファイルポインタが終端である場合、つまり最後まで読みきっている場合は処理続行不可能ということで false を返すようにしています。そうでない通常状態の場合は、指定容量分のデータを読み込み、その値を返しています。

ここまでがファイルの内容を読み込む機能を実現する read です。

----

*1)
http://jp.php.net/manual/ja/language.references.php

  • 1
  • 2

  



Pick Up Q&A

Q
動的なURLを静的に見せる方法
 このエントリーをはてなブックマークに追加 
A
普通に考えて、mod_rewrite でしょうね。 http://www.nishishi.com/blog/2006/01/mod_rewrite_url.html...

>>続きを読む

GETのままでは検索エンジンのロボットが拾ってくれなかったためにSEO対策として有効だと言われていますね。

▲解説者:岡本(アシアル株式会社 教育コーディネーター兼 システムエンジニア)