gallu’s blog

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

session_destroy()について改めて調べてみた

ちと故あって、session_destroy()の挙動を再調査してみました。
ついでに「調査手順について」備忘録的に残せると楽だったり便利だったりするかなぁ、と思ったので、少し細かく。


結論から書くと「session_destroy()は"セッションデータの破棄(≠セッションの破棄)"」であり、ついでに「session_destroy()は、おいちゃんはまず使わない」んだけど、この辺のって「調査して終わり」でナレッジが引き継がれない事が多いので、その辺の継承用に作成します。

事前準備

まずは準備から。
現在の状態をざっくりと把握しておきます。

<?php

var_dump( session_name() );
var_dump( session_save_path() );
var_dump( ini_get('session.save_path') );

//
switch(session_status()) {
  case PHP_SESSION_DISABLED:
    echo 'セッション無効';
    break;

  case PHP_SESSION_NONE:
    echo 'セッション存在せず';
    break;

  case PHP_SESSION_ACTIVE:
    echo 'セッション存在';
    break;

}

string(9) "PHPSESSID"
string(0) ""
string(0) ""
セッション存在せず


別にクラック用のテストとかではないので、大体デフォのまんまで進めます…が、セッション保存用のディレクトリだけ念のために「検証用」の場所を用意して、そちら側に退避させます。

<?php

session_save_path('/tmp/stest'); // 保存先指定

var_dump( session_name() );
var_dump( session_save_path() );

//
switch(session_status()) {
  case PHP_SESSION_DISABLED:
    echo 'セッション無効';
    break;

  case PHP_SESSION_NONE:
    echo 'セッション存在せず';
    break;

  case PHP_SESSION_ACTIVE:
    echo 'セッション存在';
    break;

}

string(9) "PHPSESSID"
string(10) "/tmp/stest"
セッション存在せず


んじゃま、実際にセッションを1つ、作って動かしてみませう。

<?php
ob_start();

function check() {
  //
  switch(session_status()) {
    case PHP_SESSION_DISABLED:
      echo 'セッション無効';
      break;

    case PHP_SESSION_NONE:
      echo 'セッション存在せず';
      break;

    case PHP_SESSION_ACTIVE:
      echo 'セッション存在';
      break;
  }
}

// セッション開始前
echo "セッション開始前\n";
session_save_path('/tmp/stest'); // 保存先指定
var_dump( session_id() ); // 一応確認用かねて
check();

// セッション開始
session_start();

// 開始後
echo "セッション開始後\n";
var_dump( session_id() ); // 一応確認用かねて
check();


ob_end_flush();

セッション開始前
string(0) ""
セッション存在せずセッション開始後
string(32) "d995c605b76f58050fb05377f410b66a"
セッション存在


ついでに

[XXXX]$ ls
sess_d995c605b76f58050fb05377f410b66a


ここら辺が開始位置。

データ設定してからsession_destroy()使ってみて挙動確認

まずなんかデータぶち込みましょう。

<?php
ob_start();

session_save_path('/tmp/stest'); // 保存先指定

// セッション開始
session_start();

//
for($i = 0; $i < 10; $i ++) {
  $_SESSION["test_{$i}"] = mt_rand(0, 999);
}

ob_end_flush();


で、確認コード作成してセッション情報を確認。

<?php
ob_start();

session_save_path('/tmp/stest'); // 保存先指定

// セッション開始
session_start();

var_dump($_SESSION);

ob_end_flush();

array(10) {
["test_0"]=>
int(411)
["test_1"]=>
int(838)
["test_2"]=>
int(626)
["test_3"]=>
int(771)
["test_4"]=>
int(225)
["test_5"]=>
int(379)
["test_6"]=>
int(184)
["test_7"]=>
int(584)
["test_8"]=>
int(166)
["test_9"]=>
int(367)
}


ちょっとファイルの中もみてみませう。

test_0|i:411;test_1|i:838;test_2|i:626;test_3|i:771;test_4|i:225;test_5|i:379;test_6|i:184;test_7|i:584;test_8|i:166;test_9|i:367;

微妙にserialize()関数で取得できそうな値っぽく見えるのですが、その辺の考察は今回の範疇外なんでいったん省略。


さてさっそくですが、session_destroy()をしてみて、各値がどう変わるのか、を、軽く見てみましょう。
まずは壊します。

<?php
ob_start();

function check() {
  //
  switch(session_status()) {
    case PHP_SESSION_DISABLED:
      echo 'セッション無効';
      break;

    case PHP_SESSION_NONE:
      echo 'セッション存在せず';
      break;

    case PHP_SESSION_ACTIVE:
      echo 'セッション存在';
      break;
  }
}

session_save_path('/tmp/stest'); // 保存先指定

// セッション開始
session_start();

// セッション破壊
session_destroy();

// 確認
var_dump($_SESSION);
var_dump( session_id() );
check();

ob_end_flush();

array(10) {
["test_0"]=>
int(411)
["test_1"]=>
int(838)
["test_2"]=>
int(626)
["test_3"]=>
int(771)
["test_4"]=>
int(225)
["test_5"]=>
int(379)
["test_6"]=>
int(184)
["test_7"]=>
int(584)
["test_8"]=>
int(166)
["test_9"]=>
int(367)
}
string(0) ""
セッション存在せず

セッションIDは消えますしsession_statusは「ない」となってますが、$_SESSION自体は、この中では有効になっています。
ちなみに実ディレクトリのほうでは「ファイルが削除」になっているので、lsしても空っぽなディレクトリが帰ってきます。


あと、http responseヘッダを確認するとわかるのですが。
実は「クライアントのCookieは消されていない」ので、セッションID自体は生きてたりします。
なので

<?php
ob_start();

session_save_path('/tmp/stest'); // 保存先指定

// セッション開始
session_start();

var_dump( session_id() );
var_dump( $_SESSION );

ob_end_flush();

こんなコードを実行すると

string(32) "d995c605b76f58050fb05377f410b66a"
array(0) {
}

となってたりしますし、この状態で、

[XXXX]$ ls
sess_d995c605b76f58050fb05377f410b66a

だったりします。


ここから、session_destroy()は「サーバ側の大本(になるファイル)を消す」一方で「クライアント側には特にそれを告知しない」作りになっている、と言えます。
まぁ実際、PHPのマニュアルにも「セッションに登録されたデータを全て破棄する」とあり、「セッションそのものを破棄する」とは書いていないので、動きとしては順当かなぁ、と。

おおざっぱにまとめと見解

そのまんまなのですが「session_destroy()は、セッションデータを破壊するよ!!」っていう動きをします。
で………


一つに「Cookieも消し去りたい!」っていうニーズが、多分、どこかにあるのかなぁ? という気が、わずかにしなくもありません*1
もしCookieも消したい場合は、別途、消し去る系のsetcookie()を流す必要があるので、適宜、流しましょう。
普段書かないんで未検証ですが。
大まかには

setcookie(session_name(), '', time() - 86400);

とか、そんな感じのを流せばよいのではないかなぁ、っと。pathとかsecure属性とかの指定は適宜よしなに空気を読んで。


もう一つが。
「ファイルが残る」ことをいとわないのであれば、セッションデータの破壊自体は

$_SESSION = array();

でも足りるっちゃぁ足りるんですね。
まぁ細かい話をすると「ファイルがあんまり残って散らかると、1ディレクトリでのファイルの数には限界があるから云々」という話になるのですが、その辺は「定期的なお掃除」で足りるし、とか色々と反論も「反論への反論」もあるので、ぶっちゃけ「状況による」としか。
session_destroy()を、まぁログアウトあたりで適切に実装したとしても「昨今、なんびとがちゃんとログアウトいたしてくださるのか」っていう根本的な問題が以下略。


あと「とはいえファイル消すじゃんそっちのほうがいいじゃん」については「その直後のアクセスで結局また同じ名前のファイルが作成されるんだよねぇ」とかいう身もふたもない系のお話が。
もちろん「Pageによってセッションが始まったり始まらなかったりしてログアウト直後の画面ではセッションが開始されない可能性」も微レ存ではあるのですが、昨今どちらかというと「全てのコードがまとめて通るような入口にsession_start()が設置されている可能性」のほうが高いので、色々と微妙。
ただ「ファイルが削除の後に生成されなおすからかえってサーバ側(のHDD)に負担になる」っていう見解も、そっちはそっちで「微々たるもんだろせいぜいにしても」って突っ込みで終わる可能性が高いので、どっちもどっち的にあんまり優位がない感じ、かと。


最後に…おいちゃんはこれが一番多いんですが。
「そもそも、セッションデータ全削除って、やる?」っていうあたり。
PHPのセッションは「無名セッション(authenticationはされてないけど、anonymousなりに"人物の特定"はなされている」とかいう、ぶっちゃけると「Cookieと等価」な動き用にもよく用いられます。
で、その状況だと、ログアウト時に「ログイン時にしか使われない情報は削除したいんだけど、ログアウトしても残しておきたい情報もある」ってのは、割とあるように思うんですねっていうかおいちゃんの案件だと大体、ある。
そうすると、「$_SESSION = array();」にしても「session_destroy()」にしても、些か「動きが派手にすぎる」ので、少々使い勝手が悪いかなぁ、と。


あと上述の派生として「部分的に消そうにも、追加があったら面倒だしそもそも散らかっていて何を消すべきかが不明」とかいう話も耳に目にしたりするのですが。
「追加があったら」については「ログイン時のみ保持、ログアウト時には削除するデータ」は、それ用にhash keyを一ついれて「その中に突っ込めばすむよねぇ」で終了。
「そもそも散らかっていて何を消すべきかが不明」については「まず仕様を整理しろ」で終了。
どちらも正直、考慮に値する内容ではないかなぁ、と。


まぁもちろん状況次第なので。
動きを把握したうえで「session_destroy()を適切に使う」のは全然よいかなぁ、と思うのですが。
上述のような理由から、おいちゃんは、session_destroy()も、「$_SESSION = array();」も、使うことはまずないですねぇ。


あ、念のため。

unset($_SESSION);

とかやると挙動が怪しくなるってお話なので、駄目! ゼッタイ!!

*1:おいちゃんは未遭遇。ただ、可能性としては捨てきれない