LoginSignup
3
1

More than 3 years have passed since last update.

【PHP】ファイルの先頭にデータを追加する&ファイル読み書きのためのシンプルな関数を作ってみる

Last updated at Posted at 2019-06-03

ファイルを読み込んだり書き込んだりする時には、file_get_contents()file()file_put_contents() が非常に便利です。

ただ、引数に色んなものを渡せるせいで、「サクッと書きたい時にまだるっこい(まどろっこしい)」という事がままありますので…

  • 引数にあれこれ渡さなくても、よく使うであろう処理をサクっと書ける
    • URL の処理等は考慮しない
  • ファイルのロック処理にもある程度気をつけてみる

あたりを意識して、コードを書いてみます。PHP 5.2.6 ~ PHP 7.3.X まで、幅広い環境で動作すると思います。


ファイルを読み込む

function file_get($path, $offset = -1, $maxLength = -1)
{
    if (!$fp = fopen($path, 'rb')) { return false; }

    flock($fp, LOCK_SH);
    $data = stream_get_contents($fp, $maxLength, $offset);
    flock($fp, LOCK_UN);
    fclose($fp);

    return $data;
}

stream_get_contents() を使えば、while でループさせる必要すらないので楽ですが、offset length の引数の順番が、file_get_contents() とは逆なところが、いかにも PHP らしい罠です(汗)1

substr() 等でも、引数は offset length の順に渡しますので、その仕様に合わせています。

使い方

//-- 単純にファイルの中身が欲しい場合
$data = file_get('/path/to/file.txt');

//-- 5byte目以降を全て読み込む
$data = file_get('/path/to/file.txt', 5);

//-- 最初の5byteだけ読み込む
$data = file_get('/path/to/file.txt', 0, 5);

ファイルへ上書きする

file_put_contents($path, $data, LOCK_EX) に該当する処理です。既に $path の中身がある場合でも、$data で上書きされます。

function file_put($path, $data)
{
    if (!$fp = fopen($path, 'cb')) { return false; }

    flock($fp, LOCK_EX);
    ftruncate($fp, 0);

    $result = fwrite($fp, $data);
    fflush($fp);
    flock($fp, LOCK_UN);
    fclose($fp);

    return $result;
}

使い方

file_put('/path/to/file.txt', $data);

ファイルの末尾へ追記する

file_put_contents($path, $data, FILE_APPEND | LOCK_EX) に該当する処理です。既に $path の中身がある場合は、$data がファイルの末尾に追記されます。

function file_append($path, $data)
{
    if (!$fp = fopen($path, 'ab')) { return false; }

    flock($fp, LOCK_EX);

    $result = fwrite($fp, $data);
    fflush($fp);
    flock($fp, LOCK_UN);
    fclose($fp);

    return $result;
}

使い方

file_append('/path/to/file.txt', $data);

ファイルの先頭へ追加する

file_put_contents() だけではできない処理です。既に $path の中身がある場合は、$data がファイルの先頭に追加されます。

function file_prepend($path, $data)
{
    if (!$fp = fopen($path, 'c+b')) { return false; }

    flock($fp, LOCK_EX);
    $data = $data . stream_get_contents($fp);
    rewind($fp);

    $result = fwrite($fp, $data);
    fflush($fp);
    flock($fp, LOCK_UN);
    fclose($fp);

    return $result;
}

使い方

file_prepend('/path/to/file.txt', $data);

注意点

軽くググってみる と見つかるコードは、ファイルロックを全く考慮していないものばかりのようですので、ご注意ください。


ファイルを配列に読み込む

UTF-8 以外の文字コードにも対応させたものです。

file() 関数では、FILE_IGNORE_NEW_LINES(各行の最後の改行を省略する)を指定する事が圧倒的に多いと思いますので、そこは指定しなくて済むようにしています。

また、FILE_SKIP_EMPTY_LINES(空行を読み飛ばす)を指定する事も多いと思いますので、空行を読み飛ばす動作がデフォルトになっています。

function file_get_array($path, $isSkipEmptyLines = true)
{
    if (($data = file_get($path)) === false) { return false; }

    $data = mb_convert_encoding($data, 'UTF-8', mb_detect_encoding($data, mb_detect_order(), true));

    return ($isSkipEmptyLines) ? preg_split('/\r\n|\n|\r/', $data, -1, PREG_SPLIT_NO_EMPTY)
                               : preg_split('/\r\n|\n|\r/', $data);
}

使い方

$array = file_get_array('/path/to/file.txt');

//-- 空行を読み飛ばさない時
$array = file_get_array('/path/to/file.txt', false);

昔話

perl や PHP4 の頃には、file_get_contents()file_put_contents() なんて関数はありませんでしたので、PHP 5 で初めてこれらを使った時は、その便利さに感動したものです。

ところが…

  • file_get_contents() ってファイルロック処理がないよね

    • きちんとロックしようとすると、ファイルオープン 1 回の中で読み込み~書き込みする必要があるので、この関数の用途的に、不要といえば不要なのかも。
  • file_put_contents() のロック処理ってきちんと働いてなくない!?

    • ※PHP5.2.6 で修正されました。c モードも、このバージョンで実装されました。
  • ファイルロック解除の際は、flock($fp, LOCK_UN) を書いたら駄目らしいよ!?

    • ※PHP5.3.2 で修正されました。アンロック処理を明示するのが正しいです。
  • file_get_contents() の第 2 引数って邪魔じゃない?

    • 特に、ファイルの一部を読みたい時に、第 2 第 3 引数に false, null と渡すのが…。
  • file_get_contents()file() には URL も渡せるけど、色々とイケてないよね…。

    • タイムアウト処理とか $http_response_header とか…。

…という、何ともむず痒い…「痒いところまで手は届くのだけど、背中を掻きたい時に、足の裏を掻いてしまう感じ」…なので、結局こういったものを用意して使ってました。2

今から 10 年ぐらい前?の話です(遠い目)

大昔のコードを元に書いてますので、おかしなところがありましたら、ご指摘ください。


  1. 歴史的に、offset パラメータが後から追加されたため、このような順番になってしまったようです。 

  2. file_get_contents() で、HTTP の POST 処理なんかまで書ける実用主義?なところは、とても PHP らしいと思います。ただ、Beauty Is in Simplicity とも言いますので…。 

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1