こんにちは!フリーエンジニアのせきです。
PHPに限らず、Webプログラミングにおいてセッションの概念は避けては通れません。ユーザーがログイン・ログアウトするようなシステムが出来るのは実はセッションのおかげなのです。
この記事では、まずセッションとは何かやセッションの使い方、セッションと言うものがどういうことかと言うことと、PHPでの具体的な利用方法について説明します。
- セッションの有効期限について知りたい
- ログイン機能でのセッションの使い方を知りたい
- セッション情報をデータベースで管理する方法を知りたい
といった応用的な内容に関しても解説していきます!
セッションとは
PHPにおけるセッション(session)の具体的な使用例について説明する前に、まずは概念そのものについて説明します。そして、それがPHPにおいてどのように使用されているかについても説明します。
セッションの概念
セッションとはWebサイトにアクセスして行う一連の行動を示します。Webサイトにアクセスしてそのサイトから出て行くかブラウザを閉じるまでが1セッションとなります。
Webサーバはセッションごとに一意のIDを持ちこれを使用することで複数ページにまたがるWebシステムであってもユーザごとの情報を保存し使い続けることができます。
このセッションを用いる方法で最も典型的なのがWebページで最もよく用いられるのがログイン・ログアウト機能です。ログインをしてからログアウトをするまでを一つのセッションと考えその間ユーザーIDやパスワードなどのステータスを保存します。
セッションを使う理由
では、セッションはなぜ必要なのでしょうか?実はWebで用いられるHTTPプロトコルには状態を管理する方法が無いからです。つまり、あるWebページに、どんな人が何回アクセスしたのかとか、どういう経緯でアクセスしたのかとった情報をHTTPプロトコルでは保持できないのです。
それを補う意味であとからセッションと言う概念が導入されたのです。PHPにおいてセッション情報を保持するための変数として、スーパーグローバル $_SESSIONが用意されています。
セッションとクッキー(Cookie)
なお、セッションと似た概念にクッキー(Cookie)と呼ばれるものがあります。これもステータスを保存するためのものですがクライアント側に全ての値を保存させますから、基本的に違う概念です。
ただ、セッションの状態を一時的にクッキーに保存することも有りますから、相互に深い関係にあることは間違いありません。ちなみに、クッキーを扱うスーパーグローバルとして、PHPには$_COOKIEが用意されています。
セッションの使い方
セッション管理を開始する
セッション管理を開始するには、session_start関数を使用します。session_startは新しいセッションを開始します。既に開始されている場合は、そのセッションを再開します。
session_start();
これを実行することで、セッション管理ができるようになります。
セッションへの変数の登録と削除
すでに説明したとおり、セッションのデータは、スーパーグローバル$_SESSIONがキーと値のペアで管理します。セッションに変数を登録するには以下のように記述します。
$_SESSION[キー名] = 値;
以下は、セッションに変数を登録するサンプルです。
<?php // セッション管理開始 session_start(); if (!isset($_SESSION['count'])) { // キー'count'が登録されていなければ、1を設定 $_SESSION['count'] = 1; } else { // キー'count'が登録されていれば、その値をインクリメント $_SESSION['count']++; } echo $_SESSION['count']."回目の訪問です。"; ?>
1回目の訪問です。
「session_start();」でセッション管理を開始します。isset関数は、変数がセットされているかどうかを返す関数です。isset($_SESSION[‘count’]」で、セッションにキー’count’が登録されているかどうかを判断しています。
登録されていなければ「$_SESSION[‘count’] = 1;」で1を登録し、登録されていれば「$_SESSION[‘count’]++;」でその値をインクリメントします。実行結果は、初めてアクセスした場合です。同じブラウザでアクセスすると、訪問回数はインクリメントされていきます。
違うブラウザからアクセスすると、それは違うセッションになるため訪問回数は1から始まります。登録した変数を削除するには、unset関数を使用して以下のように記述します。次はセッションから登録した変数を削除するサンプルです。
<?php // セッション管理開始 session_start(); // キー'count'で登録 $_SESSION['count'] = 0; // キー'count'を削除 unset($_SESSION['count']); echo $_SESSION['count']; ?>
このサンプルを実行すると、「echo $_SESSION[‘count’];」で値がないため、以下のようなエラーが発生します。
Notice: Undefined index: count in C:xampphtdocssample.php on line 21
セッションの有効期限を設定する方法
セッションには有効期限があります。有効期限を過ぎると、セッションで保持されていた情報は削除されます。
ログインしていたWebシステムを放置し、再度ログイン要求がきたり、ショッピングサイトで買い物かごに入れたまま放置し、買い物かごが空になっていたりするのは、セッションの有効期限を過ぎたためです。
デフォルト値はphp.iniの「session.cookie_lifetime」に設定されている秒数です。デフォルトでは0が設定されており、ブラウザが閉じるまで有効となります。この値はプログラムから上書きすることができます。
今回は2つの方法をご紹介します。
session_start関数による設定
1つ目はsession_start関数のオプションとして、有効期限を指定する方法です。PHP7.0.0からsession_start関数にオプションを指定し、php.iniのセッションに関する設定を上書きすることができるようになりました。
有効期限を上書きするには、以下のように記述します。
<?php session_start([ 'cookie_lifetime' => $300, ]); ?>
有効期限を秒数で指定します。
session_set_cookie_params関数による設定
2つ目はセッションを開始する前にsession_set_cookie_params関数を使用する方法です。
session_set_cookie_params(int $有効期限[, string $ドメイン上のパス[, string $ドメイン[, bool $セキュアな接続の場合のみクッキーを送信するかどうか[, bool $HTTP通信の場合のみクッキーにアクセスするかどうか ]]]])
引数について、順番に解説します。
- 有効期限
有効期限を秒数で指定します。
- ドメイン上のパス
サーバー上でクッキーを有効としたいパスを指定します。 スラッシュ(’/’) をセットすると、クッキーはドメイン配下の全てのパスで有効となります。 デフォルト値は、クッキーがセットされたときのカレントディレクトリです。
- ドメイン
クッキーを有効としたいドメインを指定します。
- セキュアな接続の場合のみクッキーを送信するかどうか
TRUEを設定すると、HTTPS接続の場合にのみクッキーが送信されるようになります。デフォルトはFALSEです。
- HTTP通信の場合のみクッキーにアクセスするかどうか
TRUEを設定すると、HTTP通信のみクッキーにアクセスをすることができ、JavaScriptのようなスクリプト言語からはアクセスできなくできます。
以下にセッションの有効期限を設定するサンプルを示します。
<?php // セッションの有効期限を5分に設定 session_set_cookie_params(60 * 5); // セッション管理開始 session_start(); if (!isset($_SESSION['count'])) { // キー'count'が登録されていなければ、1を設定 $_SESSION['count'] = 1; } else { // キー'count'が登録されていれば、その値をインクリメント $_SESSION['count']++; } echo $_SESSION['count']."回目の訪問です。"; ?>
先ほどと同じ処理をしているプログラムですが、セッション管理を開始する前に「session_set_cookie_params(60 * 5);」で、セッションの有効期限を5分に設定しています。
このように設定すると、5分間はブラウザを閉じても、同じブラウザであれば訪問回数がインクリメントされていきます。5分間アクセスがなければ、また訪問回数は1から始まります。「session.cookie_lifetime」は、セッションIDが保存されているクッキーの有効期限です。
php.iniでは「session.gc_maxlifetime」で、サーバのセッションファイルを削除する秒数の設定もできます。「session.cookie_lifetime」が0(ブラウザが閉じるまで有効)であっても、「session.gc_maxlifetime」の設定によりセッションが切れることがあるのに注意してください。
ログイン機能におけるセッションの使用方法
WebシステムのユーザID、パスワードなどによるログイン機能は、通常セッションを使用して実現しています。ログイン認証に成功したらセッションにユーザの情報を登録し、それ以降のリクエスト時にはセッションからログインしているユーザの情報を取得します。
HTMLのフォームからユーザIDとパスワードを送信し、セッションに登録するサンプルです。
login.html
<html> <head></head> <body> <form action="login.php" method="post"> ユーザID: <input type="text" name="userid" /><br> パスワード: <input type="password" name="passwd" /><br> <input type="submit" name="ログイン"/> </form> </body> </html>
login.php
<?php // セッション管理開始 session_start(); // フォームから送信されてくるユーザID $userId = $_POST['userid']; if (!isset($_SESSION[$userId])) { // 初めてのユーザはセッションにユーザIDをセット $_SESSION[$userId] = $userId; echo "ログインしました。"; } else { echo "ログイン済みです。"; } ?>
ユーザIDとパスワードを入力してログインボタンを押すと、フォームの送信先であるlogin.phpにデータが送信され、login.phpでは、ユーザIDが「$_SESSION[$userId]」、パスワードが「$_SESSION[$passwd]」で取得することができます。
このサンプルでは、初めてのアクセス時に「$_SESSION[$userId] = $userId;」で、セッションにユーザIDを登録します。
同じセッションの次回のアクセスでは「$_SESSION[$userId]」が登録されているので、ログイン済みなことがわかります
セッション情報をデータベースで管理する方法
通常、セッション情報はWebサーバにファイルとして保存されますが、これをデータベースに保存することもできます。
データベースで管理することの利点
負荷分散を考え、Webシステムを複数のWebサーバで構築した場合、セッション情報を各Webサーバのファイルに保存してしまうと、接続するWebサーバが変わるごとにセッション情報が取得できなくなってしまいます。
複数のWebサーバで共通のデータベースサーバを持ち、そのデータベースでセッション情報を管理することで、接続するWebサーバが変わってもセッション情報を引き継ぐことができます。セッション情報をデータベースに保存するメリットはそれだけでは有りません。
その他にも、
- セッション情報を可視化できる
- セッション処理の負荷分散ができる
といったメリットが有ります。大規模なサーバーを構築したい方はぜひ以下の方法をマスターしてみてください!
処理の概要
セッション情報をデータベースで管理するには、セッションIDとセッションデータを格納するテーブルを作成し、session_set_save_handler関数に、SessionHandlerInterfaceインタフェースを実装したクラスのオブジェクトを渡します。
SessionHandlerInterfaceを実装するには、以下の6つのメソッドを実装します。
- public bool close()
- public bool destroy($セッションID)
- public bool gc($セッション存続期間)
- public bool open($セッションデータ保存パス, $セッション保存名)
- public string read($セッションID)
- public bool write($セッションID, $セッションデータをシリアライズした文字列)
セッション終了時に呼び出される。
セッション破棄時に呼び出される。
セッションがメモリから削除される時に呼び出される。
セッション開始時に呼び出される。
セッションデータ読み込み時に呼び出される。
セッションデータ書き込み時に呼び出される。
サンプルコード
以下に、セッション情報をデータベースで管理するサンプルを示します。まず、以下のようなテーブルをデータベースに作成します。
CREATE TABLE TBL_SESSION ( SESSION_ID varchar(50) NOT NULL, SESSION_DATA text, CREATE_DATE int(10), PRIMARY KEY (SESSION_ID));
SESSION_IDにはセッションID、SESSION_DATAにはセッションデータをシリアライズした文字列、CREATE_DATEには最終更新日時が入ります。次に、SessionHandlerInterfaceの実装クラスを作成し、session_set_save_handlerに設定します。
<?php class MySessionHandler implements SessionHandlerInterface { // セッション終了時に呼び出される。 function close() { return true; } // セッション破棄時に呼び出される。 function destroy($session_id) { $db = get_db(); if($stmt = $db->prepare("DELETE FROM TBL_SESSION WHERE SESSION_ID = ?")){ $stmt->bind_param("s", $session_id); $stmt->execute(); $stmt->close(); $stmt = null; } $db->close(); $db = null; return true; } // セッションがメモリから削除される時に呼び出される。 function gc($maxlifetime) { return true; } // セッション開始時に呼び出される。 function open($save_path, $name) { return true; } // セッションデータ読み込み時に呼び出される。 function read($session_id) { $session_data = ''; $db = get_db(); if($stmt = $db->prepare("SELECT SESSION_DATA FROM TBL_SESSION WHERE SESSION_ID = ?")){ $stmt->bind_param("s", $session_id); $stmt->bind_result($session_data); $stmt->execute(); $stmt->fetch(); $stmt->close(); $stmt = null; } $db->close(); $db = null; if(is_null($session_data)) { $session_data = ''; } return $session_data; } // セッションデータ書き込み時に呼び出される。 function write($session_id, $session_data) { $affected_rows = 0; $create_date = time(); $db = get_db(); if($stmt = $db->prepare("INSERT INTO TBL_SESSION (SESSION_ID, SESSION_DATA, CREATE_DATE) VALUES(?, ?, ?) ON DUPLICATE KEY UPDATE SESSION_DATA = ?, CREATE_DATE = ?")){ $stmt->bind_param("ssisi", $session_id, $session_data, $create_date, $session_data, $create_date); $stmt->execute(); $affected_rows = $stmt->affected_rows; $stmt->close(); $stmt = null; } $db->close(); $db = null; return $affected_rows ? true : false; } } // データベース接続を返す関数 function get_db() { return new mysqli('localhost:3306', 'user', 'password', 'db_name'); } // MySessionHandlerをsession_set_save_handlerに設定 session_set_save_handler(new MySessionHandler(), true); // セッション開始 session_start(); $_SESSION['data'] = 0; ?>
セッションデータ書き込み時に呼び出されるwriteメソッドで、指定されたセッションIDのデータがなければINSERT、あればUPDATEをします。セッションデータ読み込み時に呼び出されるreadメソッドで、指定されたセッションIDのデータをSELECTして返します。
セッション破棄時に呼び出されるdestroyメソッドで、指定されたセッションIDのデータをDELETEします。このMySessionHandlerクラスを「session_set_save_handler(new MySessionHandler(), true);」でsession_set_save_handler関数に渡します。
その後、「session_start();」でセッションを開始し、「$_SESSION[‘data’] = 0;」でセッションに変数を登録しています。これを実行すると、TBL_SESSIONテーブルに以下のようなデータが登録されます。
SESSION_ID:'6vsmicfr6apcgfu8hqmt1ovs3e' SESSION_DATA:'data|i:0;' CREATE_DATE:'1497562961'
セッション情報をデータベースで管理することができました。なお、今回は紙幅の関係でgcを定義していません。この関係で、時間経過によるセッション削除は行われず、ブラウザを開き直すたびにセッションIDが増えてしまいます。
session_destroy() を使うことで古いセッションは一掃できますので、気になる方はこちらを利用してください。
まとめ
今回はセッションの使い方について解説しました。WEBシステムにおいて、ユーザごとの情報を保持し続けることのできるセッションは非常に重要です。セッションの使い方を忘れてしまったら、この記事を思い出して下さい。
なお、他の言語やフレームワークでも同じようなセッション管理を行っています。興味のある方は以下の記事も参考にしてみてください。