2015.7.3
0からはじめるPHP#31【Laravel5で作るデータベース#5-ファイルのやりとりと暗号化-】
最近はノリにノってるので、データベースの構築作業をガリガリ進めています。
あれ?研究は?
お陰で
データベースの検索ページは完成しました!
とりあえず今は次なる作業に向けてビューやコントローラーメソッドの整理とかやってるんですけど
(本来は最初にやるべきなのですが、勉強前なので行き当たりばったりで作ってます)それは次回の話題として、今回はファイル関係の話をしようかなと思います。
完成形としてはこんな感じです。
チェックボックスやフォームまわりに関しては大して難しくないのですが、こういった機能はどのように実現しているか、といった話をします。
まず、このリストの情報をどこに保存しているか、という話ですね。
GETだとブックマークできるって利点があるのですが、候補曲が多くなってきた際にIEだと表示しきれない可能性があります。
(URIが最大約2000字)
僕はIEあんま好きじゃないのでIEを切り捨てるって手もあるんですが、ちょっとそれは未来がないなと。まだまだIEはメジャーです。
ではPOST送信して、既存データはhiddenフォームで受け取るのはどうだ?と。
それも可能だと思うのですが、複数ページでこのデータは受け渡されていく予定なので、それはちょっと微妙かなと。GETならパラメータをURLにくっつけて回せばいいんですが、POSTだとそれはできないこともないのですが、非常に面倒ですし、直リンクに対応できません。つまり未来がない。
ということで、第三の選択肢として
クッキーが挙げられます。
ブラウザに記憶させてしまえば良い、ということになりますね。
クッキーを使おうと思った時、他にも
セッションという選択肢があります。
クッキーはブラウザ管理、セッションはサーバー管理の変数です。
どちらも似たような使い方をしますし、この例では別にどっちを使っても良さそうですが、今回は使用頻度が高そうな
セッションを使うことにします。
セッションはどうやって使うのかって話ですが
基本的にこれを宣言するだけです。
if(!isset($_SESSION["id"])){
$_SESSION["id"]=array();
}
$_SESSIONというスーパーグローバル変数が作られますので、そこに配列の形で入れて行けば良いです。
上の例では空配列を代入してますが、もちろん整数や変数も入れても構いません。使い方は普通の変数と同じです。
あとはフォームで追加するIDを送信し、それを受け取りこのセッション変数に追加していくだけでリスト追加のシステムが出来上がります。削除も同様です。
/*Session変数の削除*/
if(isset($inputs["delete_id"])){
$new_added=array();
foreach($_SESSION["id"] as $id){
$key = array_search($id,$inputs["delete_id"]);
if($key===FALSE){
$new_added[]=$id;
}
}
$_SESSION["id"]=$new_added;
}else if(isset($inputs["all_delete_send"])){
/*全削除*/
$_SESSION["id"]=array();
}
ただ、ちょっと難しいのが削除の場合です。
一定範囲の削除とかなら問題ないのですが、この例のように飛び飛びで削除される可能性がある時は処理を考えなければなりません。
この例では、新しい配列を作ってしまい、そこにフィルターをかけて残ったやつをそのまま代入する、という手を取っています。
フィルター関数を使いたければ
array_filter()とかあります。僕も最初これでやろうとしたんですが、コールバック関数内で外部の変数が使えなかったので、このような形になりました。
たぶん、クラス変数とかを用意できたら行けたと思うんですけど、そこまでしなくてもこの場合は良さそうですし。
見て分かると思いますが、全削除の場合は、対応する変数が送られてきたら空配列を代入したら全削除が行えます。
セッションの基本はこんな感じですかね。まぁ、基本的にPOST等で送られてきたデータの扱いと変わりません。
ということで、次は
ファイル操作をやってみようと思います。
まず、エクスポート・・・・・・つまり、ファイルの生成ですね。
一般に、こういうことをする際は文字列操作で抽出できるように、自分で文法を考えることになりますが、今回は
格納された変数を保存するということを目指します。
そんなことできんの?って感じですが、PHPにはあるんです。
serialize()という関数でそれができるのです。
public function export(){
session_start();//セッションの開始
$file=tempnam("./","temp-" );///一時ファイル名の取得
$data=serialize($_SESSION["id"]);//配列をテキスト化
$c_t = openssl_encrypt($data, 'AES-128-ECB', self::KEY);//暗号化
file_put_contents($file,$c_t);//書き出し
// ヘッダ
header("Content-Type: application/octet-stream");
// ダイアログボックスに表示するファイル名
header('Content-Disposition: attachment; filename="ファイル名.拡張子"');
header('Content-Length: '.filesize($file));
// 対象ファイルを出力する。
readfile($file);
unlink($file);//一時ファイル削除
exit;
}
これが
一時ファイルを作ってそれをダウンロードさせる関数になります。
さらっと見たら分かりますが
暗号化にも手を出してます。(笑)
まず、取得したい変数がセッション変数なのでセッションを開始します。
その後、一時的なファイルを作り、そのアドレスを格納します。
で、その次が肝となる部分ですが、このserialize関数により、配列をそのまま保存できる形式に変換してくれます。
unserialize()で元に戻せます。
これで後は保存するだけなのですが、ファイルが改ざんされるとアレなので、ここで
暗号化します。
暗号化する関数はいくつかあるっぽいのですが、使いやすくて安全性の高いと言われているOpenSSL形式をとります。
第三引数の
self::KEYは、クラス定数です。
class YourController extends Controller {
const KEY = "aaaaaaaaaaaa";
このように宣言します。クラス内部からの参照なのでself::となってます。
当然、復号の時も使うのでこのように定数として定義した方が良いかなと思います。
後は、ファイルに書き込んで、いろいろヘッダーを出して出力すればOKです。
file_put_contents()という便利な関数があるので是非それを使いましょう。最後に一時ファイルを削除するのを忘れないようにしましょう。
これでエクスポートをクリックすると、勝手にファイルがダウンロードされます。
生成されるファイルはこんな感じです。何のこっちゃさっぱり分かりませんね!(笑)
拡張子はいちびって.muselにしてますけど、別にtxtでも問題ないです。拡張子無しでもいいかもですね。
さて、これをインポートするにはどうしたら、って話ですね。
<form action="送信先のURL" method="post" enctype="multipart/form-data">
<input type="hidden" name="_token" value="{{ csrf_token() }}">{{--CSRF対策です。これを消すとPOST送信がブロックされます。--}}
<input type="hidden" name="MAX_FILE_SIZE" value="2000" />
<input type="file" name="up" size="30" />
<input type="submit" value="リストをインポート"/>
基本的にはこれをコピーする感じで。殆ど定型文です。見たら大体何やってるか分かりますね。
一行目はもう定型文です。POSTはGETでも良いかも知れませんけど、知りません。でも普通POSTですね。(笑)
三行目は最大ファイルサイズです。ちなみに単位はバイトです。変なファイルのアップロードを防ぎます。ただ、クライアント側で簡単に欺くことができるので注意する必要がありますが、とりあえず置いときましょう。
問題はここからで、サーバーが受け取ったデータをどうするかってことですね。僕は軽くハマったんですが、とりあえず以下のようにすれば動きます。
$inputs = \Request::all();//送られた配列データを保存
if(Input::file("up")){
//リストがインポートされる
$data = file_get_contents($inputs["up"]);
$detext =openssl_decrypt($data, 'AES-128-ECB', self::KEY);//復号
$_SESSION["id"]=unserialize($detext);//配列を元に戻し、セッションに渡す
}
一行目が必要なのかどうかはテストできないので正直分からないんですが、まぁデータはこの$inputsに入ってるので多分必要なんじゃないかな。
で、その次のif文ですね。ここがちょっとよくわからなかったんですよね。$inputs["up"]とかじゃないの?って。
でも、配列のキーを指定するだけでなんか勝手に動いてくれます。よくわかんないんですけど、まぁそういうことにしときましょう。ドツボに嵌りそうです。(苦笑)
ファイルを読み取ったら普通はfopen()とかするんですが、出力の時と同様に一気にやってくれる関数があります。ここでの引数は$inputsが必要です。どうなってるんでしょうね?^q^
で、読み取った内容を復号します。それをそのままunserialize()に通すと元の配列が出てくるので
それをそのまま配列にブチ込んでOKです。
これでセッションにIDが保存されます。意外と簡単に作れるんですね!
ちなみに、わかりきったことですが、これで確かに動きますが、このまま使ってはいけません。
もうちょっとちゃんとエラー処理とか、バリデーションなどなど、セキュリティ関連の対策も施さなければなりませんが、それはまた最後の方で組み込もうかと思います。今やってもしょーがないですし。(笑)
ということで!冒頭にちょろっとお話しましたが次回は
コントローラーとビューの整理の話をしたいと思います!