有自己在 Ubuntu 或 Debian 系統上開發 PHP 的人應該會發現,自己寫的會員登入 Remember Me 好像都無法作用。明明 Session 與 Cookie 時間都設的非常長了,卻還是會自動過期。
這是因為 Debian 會覆蓋 PHP 本身的 session.gc_maxlifetime
造成的。如果我們今天採用原生的 Session 處理機制,預設會把 session 資料存在 /var/lib/php5
裡面。
但在 Debian 中,PHP 的 session.gc_probability
被設成 0,意味著 Session 垃圾回收永遠不會進行,取而代之的是 Debian 自行設定的 Cronjob 來清除過期 Sessions,它的位置在: /etc/cron.d/php5
# /etc/cron.d/php5: crontab fragment for php5
# This purges session files older than X, where X is defined in seconds
# as the largest value of session.gc_maxlifetime from all your php.ini
# files, or 24 minutes if not defined. See /usr/lib/php5/maxlifetime
# Look for and purge old sessions every 30 minutes
09,39 * * * * root [ -x /usr/lib/php5/maxlifetime ] && [ -x /usr/lib/php5/sessionclean ] && [ -d /var/lib/php5 ] && /usr/lib/php5/sessionclean /var/lib/php5 $(/usr/lib/php5/maxlifetime)
由此可知,Debian 會每 30 分鐘清除一次 session(原生 php 的session過期時間是24分鐘),所以我們怎麼設定過期時間都是無效的。
解決方法
解決方法也不難,調整 php.ini 設定並把 cronjob 移除掉即可。不過我一向主張程式設計師不應該依賴環境設定,如果能夠由我們自己的程式在 Runtime 時覆蓋是最好的。
處理方式是重新打開 php 的 session 垃圾回收,並將 session 轉移到我們自己設定的目錄儲存,以避開 Debian 的自定清除。程式碼如下,加入在你的系統的啟動環境中即可:
// 把 session 的生命週期調到你想要的時間
ini_set('session.gc_maxlifetime', 864000);
// 打開垃圾回收,1 表示有 1% 的機會進行垃圾回收
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 100);
// 設定自己的 session path 以避開 Debian 的自動清除
session_save_path(PATH_ROOT . '/sessions');
// 都設定好之後再啟動 session
session_start();
另外,改用 Database 或其他非原生 php session 儲存方案也是方法之一。
參考資源
PHP sessions timeout too soon, no matter how you set session.gc_maxlifetime