目標
Laravel4のCookieクラス、インターフェイスはとてもわかりやすくていいんだけど、
- 値をすべてCookieに埋め込む(ある意味当然だけど)
- しかもその時暗号化する
ので、かなりあっさりRFCの4KB上限を超える。(しかもそれを例外スローなどで開発者に伝えてくれないのでハマリやすい)
なので、CookieクラスをWrapして容量無制限のHugeCookieクラスを実装してみる。
前提
- Laravel4.1 on PHP5.4/5.5
- MongoDB 2.4.9
- jenssegers / Laravel-MongoDB を導入済み(https://github.com/jenssegers/Laravel-MongoDB)
実装
WrapperクラスなのでModelとして実装してみる。ワンファイルでおしまい。
app/models/HugeCookie.php
<?php
// see : http://laravel.com/docs/requests#cookies
class HugeCookie extends Moloquent {
// see https://github.com/jenssegers/Laravel-MongoDB.
protected $collection = 'huge_cookies';
protected $softDelete = false;
protected $guarded = array();
//
const real_cookie_name = 'hgc';
/**
* Retrieve or New HugeCookie (Moloquent) Object.
*
* @return HugeCookie
*/
static function me(){
$huge_cookie_id = Cookie::get(self::real_cookie_name, null);
if(empty($huge_cookie_id) || is_null(self::find($huge_cookie_id))){
$huge_cookie = new self;
}else{
$huge_cookie = self::find($huge_cookie_id);
}
return $huge_cookie;
}
/**
* Create a new cookie instance.
*
* @param string $name
* @return
*/
static function get($name, $default_value){
$huge_cookie = self::me();
return !empty($huge_cookie->$name)? $huge_cookie->$name: $default_value;
}
/**
* Create a new cookie instance.
*
* @param string $name
* @param mix $value
* @param int $minutes
* @param string $path
* @param string $domain
* @param bool $secure
* @param bool $httpOnly
* @return \Symfony\Component\HttpFoundation\Cookie
*/
static function make($name, $value, $minutes = 60, $path = null,
$domain = null, $secure = false, $httpOnly = true){
$huge_cookie = self::me();
$huge_cookie->$name = $value;
$huge_cookie->save();
return Cookie::make(self::real_cookie_name, $huge_cookie->_id, $minutes, $path, $domain, $secure, $httpOnly);
}
/**
* Create a cookie that lasts "forever" (five years).
*
* @param string $name
* @param mix $value
* @param string $path
* @param string $domain
* @param bool $secure
* @param bool $httpOnly
* @return \Symfony\Component\HttpFoundation\Cookie
*/
static function forever($name, $value, $path = null,
$domain = null, $secure = false, $httpOnly = true){
$minutes = 2628000; // Cookie::forever() と同様に5年
return self::make($name, $value, $minutes, $path, $domain, $secure, $httpOnly);
}
/**
* Expire the given cookie.
*
* @param string $name
* @param string $path
* @param string $domain
* @return \Symfony\Component\HttpFoundation\Cookie
*/
static function forget($name, $path = null, $domain = null){
return Cookie::make($name, null, -2628000, $path, $domain);
}
/**
* Queue a cookie to send with the next response.
*
* @param dynamic
* @return void
*/
static function queue($name, $value, $minutes = 60){
$huge_cookie = self::me();
$huge_cookie->$name = $value;
$huge_cookie->save();
Cookie::queue(self::real_cookie_name, $huge_cookie->_id, $minutes);
}
}
ちょっと解説
やっていることをざっくり言うと、
- データ部の保存先をcookieではなくMongoDBにしている。(see: http://laravel.com/api/class-Illuminate.Cookie.CookieJar.html)
- で、バンドル版のCookieにはその参照キー(MongoDBのObjectID)のみを値として保存するようにする。
- インターフェイスはバンドル版のCookieを踏襲しています。(が今回私が必要だったものしか実装してません)
- extends の Moloquent というのは Laravel-MongoDB のエイリアスです。(https://github.com/jenssegers/Laravel-MongoDB#optional-alias )
使い方
http://laravel.com/docs/requests#cookies
で列挙されている使用例を真似れば特に使い方に迷うことはないでしょう。
"Cookie"を"HugeCookie"に置き換えるだけです。
HugeCookie使用例
$response = Response::make('Hello World');
$response->withCookie(HugeCookie::make('huge_data', 'Some Huge Data', $minutes));
$huge_data = HugeCookie::get('huge_data');
今回はLaravel-MongoDBに依存した実装になってますが、他のドライバーやストレージでも似たくさいことは実現できるでしょう。
以上です。Enjoy Laravel!
告知
というか宣伝ですが、このエントリーも関係してますが、
2chまとめ巡査長β
http://j.inthemaking.net/
というのを作ってみました。PCでも見れますがスマホ用です。
よろしければ暇つぶしに使って感想いただければ嬉しいです。