[ホーム] -> [Aache + PHP + PostgreSQL 実験室]

ポータルサイト(セッションの仕組み)

ポータルサイトを作ろう

先ほどの個人情報検索システムは、誰でも使えてしまいましたが、これだとやっぱり問題が多いと思います。そこで、世間一般のポータルサイトなるシステムをまねて、ログインしたユーザ毎に異なる情報を表示するようなサンプルを作ってみたいと思います。

ってあまり期待しないで下さいね。

でもその前に、セッションについて説明をしておかないといけません。

セッションとは

セッションとはいろいろな意味がありますが、Web システムのプログラミングに限定すれば、「ユーザのアクセスに対してユーザ毎に変数を保持する」という感じになります。Web システムでは、あるページにアクセスし、次のページにアクセスする場合、その間では変数は保持されません。もちろん、GET、POST を使って値を渡すことは出来ますが、「PHP の変数の保持」にはほど遠いものがあります。

例えば、My Yahoo などのようなポータルサイトや、hotmail などのようなフリーメールは、使うときに最初にログインをします。次のページに移っても、ログインしたユーザ固有のページを表示します。フリーメールでは、ログインするとそのユーザの受信メールが表示され、メールを作成するとそのユーザの名前で作成されます。

ということは、ログインしたときの情報をどこかに持っているはずです。しかし、GET や POST でログイン情報を受け渡している様子はありません。これを行っている内部の仕組みを、セッションと言います(おおまかにね)。

多くのシステムでは、ログイン時などに、サーバ側でセッションを作成します。クライアント(ブラウザ)には、サーバ側で作成したセッション固有の ID を送ります。この ID をセッション ID と呼び、大抵の場合はクッキーとしてセッション ID をクライアントに送ります。

クライアントは、セッション ID が送られてくると、次のアクセスからセッション ID も一緒に送ります(セッション ID がクッキーだから)。サーバ側は、送られてきたセッション ID に該当するセッションがあると、クライアントとセッションを結びつけます。これにより、サーバ側はユーザを特定でき(セッション ID は重複しない)、セッションをプログラムから使えるようにします。

このセッション ID のおかげで、複数のページにまたがり変数を保持したり、ユーザ毎に固有の変数を保持したり出来るのです。

だから、ポータルサイトやフリーメールの多くは、クッキーを受け入れる設定にして下さいとアナウンスしています。

PHP では、このセッションの仕組みをサポートしていて、session_ で始まる関数群がそれにあたります。次から使い方を見ていきましょう。

セッションの使い方

まずは、画面上から値を入力し、その値をセッションに格納し、他のページからでもその値を参照できるのを確認してみましょう。

と、その前に確認しておくことがあります。PHP のデフォルトのセッションの仕組みだと、セッションに格納した値をあるディレクトリ内に、ファイルとして保存します。従って、ディレクトリに書き込みできないといけません。

このディレクトリは、php.inisession.save_path という設定項目に指定されています。このディレクトリに対して、Apache の実行ユーザ(httpd.confUser, Group ディレクティブで指定したもの)で書き込みを行います。従って、このユーザで書き込みできるように設定する必要があります。Apache を nobody ユーザで動くように設定してあって、php.inisession.save_path = /var/run/php と指定した場合、

> su -
# cd /var/run
# mkdir php
# chown nobody
# chmod 700 php

この設定で大丈夫だと思います。まず、セッションに保存する説明です。

// セッションに変数を登録(暗黙的にクッキーを出力)
session_register("s_name");
// 変数に値を格納
$HTTP_SESSION_VARS["s_name"] = "abc";

session_register でセッションに保存する名前を登録します。HTTP_SESSION_VARS という連想配列に、先ほど保存した名前をキーに値をセットします。これだけでセッションに保存されます。

次は、この保存された値を表示してみましょう。

// セッションを開始(セッション ID とセッションを結びつける)
session_start();
// 値を表示
echo($HTTP_SESSION_VARS['s_name']);

session_start 関数を実行することにより、クライアントから送られてきたセッション ID を元にセッションを結びつけ、HTTP_SESSION_VARS 変数を使って値にアクセスできるようになります。とても簡単ですね。

ちなみに、session_start は、クッキーを出力するために HTML などの文字が出力される前に呼び出す必要があります。また、session_register も、session_start が呼ばれていない場合は、クッキーを出力します。したがって、これらのファイルの先頭に書くと良いでしょう。

session_register を途中に書きたい場合は、ファイルの先頭で session_start を呼び出しておけば大丈夫です。

この時点で、先ほど作成したディレクトリ(/var/run/php)の下を見ると、ファイルが出来ているのが分かります。

明示的にセッションを破棄するには次のようにします。

// セッションを開始(セッション ID とセッションを結びつける)
session_start();
// 結びついているセッションを破棄
session_destroy();

これで、セッションが完全に破棄されました。もし、破棄ではなく、セッションに関連づけられている値だけを破棄したい場合は、session_unset を使います。この関数は、前にもしセッションに変数が保存されていたら、それを全部クリアーして初期化したい場合などに利用します。例えばログインページとか。なので、ログインを安全に行うには、次のようにします。

session_start();
session_unset();
session_register("s_name");
$HTTP_SESSION_VARS["s_name"] = "abc";

セッションのサンプル

それでは、実際のプログラムを作ってみましょう。

ポータルサイト

先ほどは、個人情報検索システムを作りましたが、ここでは超簡易ポータルサイトを作りたいと思います。実装する機能は次の機能です。

これだけです。サンプルだから簡単な方がいいですね。利用するテーブルですが、二つのテーブルが必要です。

一つ目はユーザデータを格納しておくテーブルで、ここには、ユーザ ID、パスワード、名前を格納しておきます。二つ目は、ブックマークを格納しておくテーブルです。こちらには、通し番号、URLに付ける名前、URL、登録したユーザ ID を格納します。

-- ユーザマスタ
create table portal_user (
  userid   varchar(10) primary key,
  password varchar(30) not null,
  name     varchar(30) not null
);
-- ブックマーク
create table user_bookmarks (
  code     integer      primary key,
  name     varchar(100) not null,
  url      varchar(300),
  userid   varchar(10)
      references portal_user(userid),
);

で、このテーブルを元に、ユーザ申請の機能と、ブックマークを管理する機能を作ったので、見てみて下さい。

ソースはこちらからどうぞ

このサンプルでは、テーブルにアクセスするユーザとして、「www」というユーザを用意しています。テーブルの作成自体は、自分が普段 PostgreSQL を利用するときに使用しているユーザアカウントで構いませんが、PHP からアクセスするときは、www というユーザで接続することにします。これは、万一 PHP にバグがあっても、被害が大きくならないようにするためです。(なるべくバグが無いように作っているつもりですが)

クッキーを受け入れない

このセッションを使うに当たり、クッキーは重要です。しかし、人によってはクッキーを受け入れない設定にしていたり、クッキーの使えないブラウザもあります。パソコンで動くブラウザのほとんどはクッキーを利用できるはずですが、多くの携帯電話は今のところクッキーが利用できません。

こういった場合、別の手段を用いる必要が出てきます。別の方法とは、セッション ID を、GET、POST の度に受け渡す方法です。これには、session_name, session_id 関数を使います。

リンクの場合
next.php?<?= session_name() ?>=<?=session_id() ?>
FORM の場合
<input type="hidden" name="<?= session_name() ?>" value="<?= session_id() ?>">

これをすべての箇所に使わないといけません。ちょっと面倒ですね。ですが、PHP をコンパイルするときに、--enable-trans-sid を付けてコンパイルしておくと、この作業が自動で行われます。

自動で行われる場合、session_start 関数を呼んだときに、クッキーにセッション ID が無ければ、クッキーにセッション ID をセットすると共に、そのページのすべてのリンク、フォームにセッション ID が渡るように HTML を書き換えます。もし、session_start を呼んだときにクッキーにセッションが渡ってきていたら、ページの書き換えは行いません。したがって、最小限の労力でクッキーの利用できないクライアントに対応することが出来ます。

ただし、この方法でも、途中に普通の HTML を含めることが出来ないので(普通の HTML はセッション ID を渡さない)、気を付ける必要があります。

セッションの設定

セッションを制御するために php.ini で設定する設定項目があります。すべての項目について知りたい場合はマニュアルを参照してもらうとして、ここでは主要なものだけ説明しておきます。

セッションの設定(一部)
キー 説明
session.save_path セッションデータを保存しておくパスを設定します。上でも説明しましたが、Apache の実行ユーザが書き込みできるディレクトリである必要があります。
session.use_cookies セッション ID を保持するのに、クッキーを利用するかどうかを指定します。デフォルトは On です。
session.cookie_lifetime クライアントが保持するクッキーの生存期間を、秒数で指定します。デフォルトは 0 で、ブラウザを終了するまでです。例えば 3600 を指定すれば、ブラウザを閉じてしまっても、一時間有効になります。
session.cookie_path クッキーを有効にするパスです。ブラウザは普通、ここで指定したパス以下に対するアクセスにしか、クッキーを送りません。例えば、/app/portal とすれば、/app/portal/list.php に対するアクセス時にはクッキーを送りますが、/dokoka/hoge.php に対するアクセス時にはクッキーを送りません。デフォルトは / で、そのサーバ内に対するすべてにクッキーを送ってしまいます。ディレクトリごとに別の Web アプリケーションを作っていて、それぞれでセッションを使いたい場合などは、適切なパスを指定しましょう。
session.cookie_domain こちらは、クッキーを有効にするドメイン名です。デフォルトはセットされていないため、同じマシンに対してしかクッキーを送ってきません。もし、domain.com と指定すると、www.domain.com, private.domain.com などにクッキーを送るようになります(session.cookie_path で指定したパスにも一致すれば)。まあ、セッション ID の場合、他のマシンに送っても意味がないと思いますが・・・。
session.cache_limiter nocache, private, public のいずれかが指定できます。これは、セッションを使ったページをキャッシュ出来るかどうかの指定です。nocache がデフォルトですが、この設定だとブラウザにもキャッシュされないため、戻るボタンを押したときにも再読込が必要になります。private はブラウザにのみキャッシュされます。public は、キャッシュサーバ(プロキシサーバ)もキャッシュ出来るようになります。これは、HTTP レスポンスヘッダーにより制御しているので、適切に解釈しないソフトを使っている場合はこの限りではありません。
session.cache_expire これは、上で private, public を指定したときに、キャッシュ出来る時間を分で指定します。デフォルトは 180 分です。
session.gc_maxlifetime セッションを破棄するまでの時間を秒で指定します。最後にセッションにアクセスしてから、この秒数が経過すると、そのセッションは破棄されます(破棄されると、保存した変数にはアクセスできません)。あまり短い間隔だと、ユーザがちょっと別のことをしている隙に、セッションが破棄されてしまうかもしれません。あまり長い間隔だと、セキュリティ上問題が出てきます。デフォルトは 1440 秒です。長い間隔を指定する場合は、session.cookie_lifetime とのかねあいも必要です。
session.gc_probability セッションを破棄するかどうかチェックする確率を % で指定します。リクエストに対して、この確率でセッションを破棄するかどうか(session.gc_maxlifetime を過ぎているセッションがないかどうか)のチェックを行います。アクセスがたくさんのあるサイトは、少なめにした方がいいでしょう。
ホームへ