【PHP】配列、オブジェクトをファイルに保存する方法
PHPで配列やオブジェクトを扱う際、テキストファイルなどにそのデータを保存(書込み)したいときがあります。
その方法はコーディング次第でいろいろと書けますが、現段階で私がスマートだと思うコードをまとめたいと思います。
ファイルへの配列・オブジェクトの保存
配列・オブジェクトをファイルに保存したいときの用途は2つかと思います。
- 保存した後に再度データを扱う
- 保存した配列・オブジェクトの中身を確認したい
1.保存した後に再度データを扱う場合
この場合、現段階で私がもっともスマートだと思うコードはこちらです。
<?php
//ファイルに保存したいデータ(文字列、配列、オブジェクトなんでもOK)
$data = "";
//ファイルのパス
$file_path = dirname(__FILE__) . "/data-base.txt";
//シリアル化
$data_serialize = serialize($data);
//ファイルに保存
file_put_contents($file_path, $data_serialize, LOCK_EX);
//保存したファイルのパーミッションを644にする
chmod($file_path, 0644);
?>
実際は8~12行目をまとめてこのようにしています(1行でも短く書けるため)。
file_put_contents($file_path, serialize($data), LOCK_EX);
このコードを利用すると、データが文字列だろうが数値だろうが配列だろうがオブジェクトだろうがすっぽりファイルに保存できます。
例えば、「$data」の部分に次のような連想配列を入れて実行すると、
$data = array("a"=>"ひつじ", "b"=>"うま", "c"=>"ねこ", "d"=>"いぬ", "e"=>"ライオン");
データがシリアライズされて保存されます。
a:5:{s:1:"a";s:9:"ひつじ";s:1:"b";s:6:"うま";s:1:"c";s:6:"ねこ";s:1:"d";s:6:"いぬ";s:1:"e";s:12:"ライオン";}
このコードのポイントはserialize関数を使っているところです。
値の保存可能な表現を生成します。
型や構造を失わずに PHP の値を保存または渡す際に有用です。
とあるように、保存したデータをそっくりそのまま取り出せます。
保存したデータを取り出す
ファイルに保存したデータは次のようにunserialize関数でそっくりそのまま取り出すことが可能です。
<?php
//ファイルからデータを取り出す
$data_serialize = file_get_contents($file_path);
//元のデータに戻る
$data = unserialize($data_serialize);
?>
注意
ただし注意点もあって、リファレンスに
アンシリアライズの時には、オブジェクトのインスタンス生成やオートローディングなどで コードが実行されることがあり、悪意のあるユーザーがこれを悪用するかもしれない
とあるように、自己で完結するプログラムではなく他者が関わる場合は注意が必要です。
その場合は、json_encode関数とjson_decode関数を使います。
【データの保存】
<?php
//ファイルに保存したいデータ(文字列、配列、オブジェクトなんでもOK)
$data = "";
//ファイルのパス
$file_path = dirname(__FILE__) . "/data-base.txt";
//JSONエンコード
$data_jsonencode = json_encode($data);
//ファイルに保存
file_put_contents($file_path, $data_jsonencode, LOCK_EX);
//保存したファイルのパーミッションを644にする
chmod($file_path, 0644);
?>
例えば、「$data」の部分に次のような連想配列を入れて実行すると、
$data = array("a"=>"ひつじ", "b"=>"うま", "c"=>"ねこ", "d"=>"いぬ", "e"=>"ライオン");
データがJSONエンコードされて保存されます。
{"a":"\u3072\u3064\u3058","b":"\u3046\u307e","c":"\u306d\u3053","d":"\u3044\u306c","e":"\u30e9\u30a4\u30aa\u30f3"}
【データの取り出し】
<?php
//ファイルからデータを取り出す
$data_jsonencode = file_get_contents($file_path);
//JSONデコードして元のデータに戻す
$data = json_decode($data_jsonencode);
?>
2.保存した配列・オブジェクトの中身を確認したい
プログラムを書いているとき、要所要所で配列やオブジェクトの中身を確認したいときがあります。
しかし、serialize関数やjson_encode関数を使ってファイルに保存すると目視でのデータ確認がしにくくなります。
その場合は次のようにすればよいです。
<?php
//ファイルに保存したいデータ(文字列、配列、オブジェクトなんでもOK)
$data = "";
//ファイルのパス
$file_path = dirname(__FILE__) . "/data-base.txt";
//ファイルに保存
file_put_contents($file_path, print_r($data,true), LOCK_EX);
//保存したファイルのパーミッションを644にする
chmod($file_path, 0644);
?>
保存したファイルを開くと次の例のように視認性よく配列、オブジェクトがそのまま出力されていると思います。
Array
(
[a] => ひつじ
[b] => うま
[c] => ねこ
[d] => いぬ
[e] => ライオン
)
※ダメなパターン※
備忘録として、最初に考えたことを載せておきます。
<?php
//ファイルに保存したいデータ
$data ="";
//ファイルのパス
$file_path = dirname(__FILE__) . "/data-base.txt";
//テキストファイルに保存
file_put_contents($file_path, $data, LOCK_EX);
//保存したファイルのパーミッションを644にする
chmod($file_path, 0644);
?>
これを見てわかるように、9行目にunserialize関数やjson_encode関数が使われていません。
「$data」の部分に改行コード「\n」があれば
$data ="ひつじ\nうま\nねこ\nいぬ\nライオン";
下のようにきれいにファイルに保存できます。
ひつじ
うま
ねこ
いぬ
ライオン
しかし、「$data」が配列であれば、
$data = array("ひつじ", "うま", "ねこ", "いぬ", "ライオン");
次のように配列の要素が全てつなげられて文字列となります。
ひつじうまねこいぬライオン
このとき、 implode関数(参考:【PHP】配列の値(要素)を指定文字で区切って文字列にする)を使って特定文字で区切りながら文字列にするとよいのでは?と考えて次のようにしました。
$data = array("ひつじ", "うま", "ねこ", "いぬ", "ライオン");
$data = implode("\n", $data);
するとうまくファイルに保存できました。
ひつじ
うま
ねこ
いぬ
ライオン
また、「$data」の部分が連想配列やオブジェクトの場合、
$data = array("a"=>"ひつじ", "b"=>"うま", "c"=>"ねこ", "d"=>"いぬ", "e"=>"ライオン");
$data = implode("\n", $data);
ひつじ
うま
ねこ
いぬ
ライオン
となり、連想配列のキーが消去されて出力されます(キーも再利用したいときはこれでは不適)。
- 文字列
- 配列
- 連想配列・オブジェクト
それぞれに対応するコードにするのは面倒なので、最終的に9行目にunserialize関数やjson_encode関数を使用することにしました。
「file_put_contents」関数は有能
ここからは私個人の覚書をします。
これまで私はなんらかのデータをファイルに保存する際、次のコードを書いていました。
<?php
//テキストファイルに保存したいデータ
$data ="";
//テキストファイルのパス
$file_path = dirname(__FILE__) . "/data-base.txt";
//ファイルハンドラ作成(wは上書きモード、w+は上書き書込+読込モード)
$file = @fopen($file_path,"w");
//書き込み時にファイルをロックする
flock($file, LOCK_EX);
//書き込み
fputs($file, $data);
//ロックを開放
flock($file, LOCK_UN);
//ファイルを閉じる
fclose($file);
?>
毎回コピペ対応してたのですが、毎回「長すぎてコードが見にくいな~」って思っていました。
なぜ、 他にもファイル保存の関数があるのにこのコードを使っていたのかというと、「LOCK_EX」で排他ロックを指定できたからです。
排他ロックって何?
排他ロックとは、複数の処理が同時に一つのファイル(ここでは「data-base.txt」)に書き込んだり読み込んだりするときにデータの整合性がずれるのを防ぐ機能のことです。
どゆこと?
具体的に 「LOCK_EX」 の働きを説明すると、ある処理が最初にファイルをオープンしたら、他の処理がそのファイルにアクセスしようとしても、最初の処理が終わるまで処理待ちにさせる機能です。
なので、一つのファイル(データ)があちゃこちゃ読み書きされるのを防ぎ、データの整合性がとれます(しかもOSのファイル読み書きの仕組みを利用しているので高速!)。
ちなみに、「 LOCK_SH 」を指定すると共有ロックとなり、読み込みは可能ですが、書込みは不可となります。
また、fopen関数でさまざまなmodeを指定できるので詳しく知りたい方はリファレンスをみてください。
「file_put_contents」で一撃
しかし、先日file_put_contents関数のリファレンスを見ていた時に、
この関数は、fopen()、fwrite()、 fclose() を続けてコールしてデータをファイルに書き込むのと等価です。
とありました。つまり先ほどのコードは次のコードと同じ機能ということになります。
<?php
//テキストファイルに保存したいデータ
$data ="";
//テキストファイルのパス
$file_path = dirname(__FILE__) . "/data-base.txt";
//ファイルに保存
file_put_contents($file_path, $data, LOCK_EX);
?>
そして、第3引数に「LOCK_EX」も指定できたのです。
書き込み処理中に、ファイルに対する排他ロックを確保します。 つまり、fopen() の呼び出しから fwrite() の呼び出しまでの間に flock() の呼び出しが発生するということです。
また、file_put_contents関数の第3引数には「 FILE_APPEND」( 保存先にファイルがすでに存在する場合、上書きするするのではなく追記する)も指定できます。
そして、 これら2つのフラグはOR演算子で同時に指定できるのです(つまり「w+」モードになる)。
LOCK_EX|FILE_APPEND
file_put_contents関数を使えば長々としたコードを書くことなく自分がしたいことができるのでfile_put_contentsはかなり有能だなって思いました。
以上、PHPで配列やオブジェクトをファイルに保存する方法でした。
あわせて読んでほしい!
1:ツイ消しツールが:2020/06/13 19:01:52
全データ読み込みできないバグが発生しております。コメント欄にも多数寄せられていますので、ご確認いただけると幸いです。