cookieをおさらいしてみた

2010/11/11 | cookie

何を今更という感じだが、わかっているようでわかっていないクッキー(cookie)のおさらいをしてみた。まずPHPでセットしたクッキーについて調べてみた。

PHPでクッキーをセット(setcookie)すると、その情報はHTTPヘッダとしてブラウザに送出される。HTTPヘッダとして送出するための関数であるから、その関数よりも前に、ブラウザに対してはヘッダ以外の情報を出力しているとエラーになる。出力するHTTPヘッダーはこんな感じになっていた(この例はsetcookieではなくセッション開始の例)。

HTTP/1.1 200 OK
Date: Thu, 11 Nov 2010 01:26:46 GMT
Server: Apache
Set-Cookie: PHPSESSID=i7y59fnv2cphp1kf76bdodu4t8ii6elk; path=/; HttpOnly
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 2402
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8

ブラウザに対してSet-Cookieヘッダを送っている。ちなみにsetcookie関数ではなく、header関数で直接上記の文字列を送出しても問題ないはずだ(試していないけれど)。送出された情報を見ると、値(この場合はセッションID)、パス、HttoOnly、期限(ブラウザを閉じるまで)の情報が送られている。またクッキーはデフォルトで、当該ホストとのみやりとり可能なので、アクセスしたホスト名に限定したクッキーとなっているはずだ。

サーバから上記のヘッダを送出されると、ブラウザを閉じない限り当該ホストに関して、ブラウザは今後永久に(クッキーの削除でもしない限り)、Cookieリクエストを送出するようになる。リクエストの例はこちら。

GET /dummy.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; ja; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12 GTB7.1 ( .NET CLR 3.5.30729; .NET4.0C)
Accept: text/css,*/*;q=0.1
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Referer: http://www.example.com/
Cookie: PHPSESSID=i7y59fnv2cphp1kf76bdodu4t8ii6elk

/dummy.htmlをリクエストした例だ。リクエストの最後に、先程受信したクッキーを送出しているのが見て取れる。一旦クッキーをセットされると、ブラウザが閉じられるかそのクッキーが削除されるか有効期限が切れるまでは、ブラウザは当該ホストに対して必ずクッキー付きでリクエストを送るようになる。

以下は、/dummy.html内に含まれる/img/logo.pngに関するリクエストだ。

GET /img/logo.png HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; ja; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12 GTB7.1 ( .NET CLR 3.5.30729; .NET4.0C)
Accept: image/png,image/*;q=0.8,*/*;q=0.5
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Referer: http://www.example.com/
Cookie: PHPSESSID=i7y59fnv2cphp1kf76bdodu4t8ii6elk

クッキーの送出は対象が何であっても関係ない。拡張子も関係ない。プログラムであろうが、HTMLであろうが、画像であろうが、音楽であろうが、ブラウザが閉じられるかそのクッキーが削除されるか有効期限が切れるまでは、ブラウザは当該ホストに対して、ずっとクッキー付きでリクエストを送り続ける(そもそもブラウザがリクエストする時点でリクエスト対象に対して「これは画像」「これはHTML」とか淡い期待をいだいているだけで、本当にそういうものが返ってくるかどうか分からないから)。

自分で整理してみて、ちょっとだけ理解できた。あとはJavaScriptによるcookieの受け渡しを調べなきゃな。

session_set_cookie_params関数

PHPしかやっていなかったときは、極力クッキーを使って値を保存するようなことはしないようにしていた(セッションは使うけど)。クライアントサイドにデータを保存しても改ざんされる前提で使わないといけないからだ。しかし最近はいろいろ使うようにしている。そもそもGETやPOSTでの値引渡しと同じという前提でやれば問題ないわけで。ただ、GETやPOSTと違うのは値をセットする方法で、setcookie関数を使わないといけないところだ。GETやPOSTと同様にこんな感じでクッキーがセット出来ればいいのに。

$_COOKIE[‘_key’]=’_value';

まぁ、できないほうがいいという理由もわかるのだけれど。

で、最近使うようになったクッキーだが、便利な関数があることを知った。session_set_cookie_params関数だ。この関数一つでクッキー周りの設定がすべてできてしまう。

void session_set_cookie_params (
int $lifetime
[, string $path
[, string $domain
[, bool $secure = false
[, bool $httponly = false ]]]]
)

第1引数:セッション有効期限(秒)
第2引数:クッキーが動作するパス
第3引数:クッキーが有効なドメイン名
第4引数:https時のみ使用可能とするかどうか
第5引数:JavaScript等からのクッキーへのアクセスを許可するかどうか

デフォルトではPHPでセットしたクッキーを、JavaScriptから読み込むことはできないのかな。その逆はできるけど。

「この記事を読んだ人はこんな記事も見ています」を実現する

2010/11/06 | データベース

レコメンデーションの仕組みで一般的となっている「この記事を読んだ人はこんな記事も見ています」。これをSQLで実現するにはどうすればいいかを模索していて、少し先が見えてきたのでメモしておく。

まずテーブルとしてusers_pagesを作成し、カラムは2つ(user_idとpage_id)。カラムの型はそれぞれinteger。本来はusersテーブルとpagesテーブルがあるが、とりあえずここでは考慮しない。user_idとpage_idの組み合わせを主キーとし、それぞれのカラム単体でもインデックスを張っておく。こんな感じ。

CREATE TABLE `users_pages`
(
`user_id` int(11) NOT NULL,
`page_id` int(11) NOT NULL,
PRIMARY KEY (`user_id`,`page_id`),
KEY `user_id` (`user_id`),
KEY `page_id` (`page_id`)
);

訪問者が訪れるたびに、user_idを発行し、user_idがページを閲覧するたびに、当該user_idと閲覧したページの番号(page_id)の組み合わせを行として挿入していくイメージ。

「この記事を見た人は」ということなので、記事ごとにSQL文を流す方法を考える。つまりpage_id=n(nはページ番号)。一番簡単に思いつくのはこんな感じと思う。

  1. 当該page_idを閲覧したuser_idを検索する
  2. 検索結果のuser_idが閲覧したpage_idを検索する(ただし当該ページは除く)
  3. page_idでGROUP BYして件数をカウントする
  4. ORDER BYで降順にソートする

つまりサブクエリを使う方法。しかしこれはどうも速度が出ない。1万件程度のデータ(ページ数500平均5ページビューとして2,000人のデータ)で以下のSQL文を実行してみた(nはページ番号)。

SELECT * FROM users_pages up1 WHERE up1.page_id IN (SELECT up2.user_id FROM users_pages up2 WHERE up2.page_id=n);

実行時間は・・・数分たっても結果が帰ってこなかった(非実用的)。次に自己結合を使って書き直してみた。

SELECT * FROM users_pages up1 LEFT JOIN users_pages up2 ON up1.user_id=up2.user_id WHERE up2.brand_id=n;

クエリ実行時間0.01秒。こちらは実用的な速度。後者のSQLでpage_id=nを閲覧した人が、他に閲覧したpage_idを抽出することができる。

ただこれ、50万件のデータになると処理速度がクエリ実行時間が1.25秒になった。ブラウザでSQLを流してから結果を受け取るまではすごく時間がかかった。「SELECT *」ではなくて必要なカラムを指定すれば早くなるけど。おそらく数万件程度のデータなら都度実行してもよさそうだが、それ以上はバッチ処理にしておかないとダメそう。COUNTしてORDER BYするともっと遅そうだから。

SQL苦手だな・・・。

ウェブ屋で集まる会(参加者募集)

2010/11/01 | その他

このブログに訪れる人はきっとプログラマが多いだろうと思ったので。PHPな人、JavaScriptな人に是非。

去年、自分で「ウェブ屋の飲み会」を企画・主催した。それが結構好評で、去年のメンバーと話していて「今年もやろう」ということになった。去年はプログラマが4人ほど、HTMLコーダーが2人ほど、デザイナーが2人ほどを集めた。自分ともう一人が、普段付き合いのある(もしくは最近知り合った)ウェブ屋さんをそれぞれ声をかけてメンバーを集めた。結果集まった人同士はほとんどが初対面。でもとりあえずウェブ業界という共通のくくりの中にいる人ばかりなのでそれなりに盛り上がった。

その後仕事でつながったりしている人達もいて、当初の目標(人をつなげる)を達成できた感があった。

今年は10人超くらいでメンバーを集めようとしているけれど、今のところデザイナーさん、コーダーさんは集まりそうだけど、声を掛けるべきプログラマさんがいなくて・・・。というわけで、テクメモで声をかければ誰か来るんじゃないかと思って記事にしてみた。

日程は12月の1週目か2周目を予定していて、場所は吉祥寺あたり。最近のウェブ制作で一番よく使われていそうなPHPもしくはJavaScriptをメインにしている人を探しています。でもデザイナーさんとかコーダーさん、フラッシャーさんでもOK。ただしあまり人数を多くしたくないので、たくさんの人数になっちゃった場合は後日別口で場を設けます。現在お声がけ中のメンバーは30代前半から50代までくらい。男女それぞれ。

今のところメンバーは、それぞれ相応にスキルがあるので、ウェブ業界駆け出しの人はあまり楽しくないかもしれない。プログラマの目安を言うと、PHPなら自分で汎用的なクラスを書けること、JavaScriptならAjaxとか普通にできちゃう人。あと社交的でお酒と会話を楽しめる人だとメンバーと打ち解けやすいと思う。男女は問わず。何でも屋さん的な人はこの集まりに向かないので、ずばり「これにはそこそこ自信があるよ」というのを持っている人がいいです。

参加してみたい人はコメント欄に入力してください。メールアドレスもよろしくです。連絡いただいたコメントは公開しません。個別にメールで連絡します。では、誰か、よろしくです。

getBoundsがundefined

2010/10/31 | JavaScript/Ajax

今まではほぼPHP専門でやってきたけど、最近はあきらかにJavaScriptの開発が多い。JavaScriptで作ったプログラムが動くと、見た目の印象がいいので面白いしね。

Google Maps V3で開発していて、地図表示領域の四隅の座標を取得したくて以下のように記述してみた。

var bounds = gmap.getBounds();
// gmapはGoogle Mapsのオブジェクト

四隅の座標なんて簡単!、と思っていたら、値が取得できない。ていうかboundsがundefinedになってる。その直前でgmap.getZoom()出来ているのに!。

検索してみてわかったこと。どうもgetBounds()は、タイルのロードが完了していないと取得できないらしい(英語のサイトになんとなくそんな意味のことが書いてあった)。「addListenerして’bounds_changed’とか’tilesloaded’とかしろ」と書いてある。

結構不便だけど、考えて見れば当然かもしれない。地図の表示領域はブラウザの画面サイズやCSSとも関係してくるだろうから、タイルがロードさえされればそこから判別できるんだけど、と言われれば納得出来る。

納得出来るのと、こんな具合に動いて欲しい、というのは時として矛盾する。


守谷市(まちの情報ポータル) 無料アンケートレンタルjpForm.net