PHP Conference Japan 2024

Session 函式

目錄

新增註解

使用者貢獻的註解 20 則註解

14
Edemilson Lima <pulstar at gmail dot com>
17 年前
Session 和瀏覽器分頁

您可能已經注意到,當您在 Firefox、Opera、IE 7.0 中開啟網站的兩個或多個分頁,或者在 IE 6.0 中使用「Ctrl+N」開啟新視窗時,它會使用相同的 cookie 或傳遞相同的 session id,因此另一個分頁只是前一個分頁的副本。您在一個分頁中所做的操作會影響另一個分頁,反之亦然。即使您再次開啟 Firefox,它也會使用先前 session 的相同 cookie。但這通常不是您所需要的,特別是當您想在網頁應用程式中的一個位置複製資訊到另一個位置時。發生這種情況是因為預設 session 名稱是 "PHPSESSID",所有分頁都會使用它。有一個解決方法,它僅依賴於變更 session 的名稱。

將這些行放在您主要腳本(呼叫子腳本的腳本)的頂部,或您擁有的每個腳本的頂部

<?php
if(version_compare(phpversion(),'4.3.0')>=0) {
if(!
ereg('^SESS[0-9]+$',$_REQUEST['SESSION_NAME'])) {
$_REQUEST['SESSION_NAME']='SESS'.uniqid('');
}
output_add_rewrite_var('SESSION_NAME',$_REQUEST['SESSION_NAME']);
session_name($_REQUEST['SESSION_NAME']);
}
?>

運作方式

首先,我們比較 PHP 版本是否至少為 4.3.0(在此版本之前,函數 output_add_rewrite_var() 無法使用)。

之後,我們檢查 $_REQUEST 陣列中的 SESSION_NAME 元素是否為格式 "SESSIONxxxxx" 的有效字串,其中 xxxxx 是由腳本產生的唯一 id。如果 SESSION_NAME 無效(即尚未設定),我們會為其設定一個值。

uniqid('') 會為新的 session 名稱產生一個唯一的 id。它不需要像 uniqid(rand(),TRUE) 那樣太強,因為所有的安全性都依賴於 session id,而不是 session 名稱。我們在這裡只需要為每個開啟的 session 提供不同的 id。即使使用 getmypid() 也足夠,但我不知道這是否會對 Web 伺服器造成威脅。我不這麼認為。

output_add_rewrite_var() 會自動將一組 'SESSION_NAME=SESSxxxxx' 新增到您網站中的每個連結和 Web 表單。但為了使其正常運作,您需要將其手動新增到您擁有的任何 header('location') 和 Javascript 程式碼,如下所示

<?php
header
('location: script.php?'.session_name().'='.session_id()
.
'&SESSION_NAME='.session_name());
?>
<input type="image" src="button.gif" onClick="javascript:open_popup('script.php?<?php
echo session_name(); ?>=<?php echo session_id(); ?>&SESSION_NAME=<?php echo session_name(); ?>')" />

最後一個函數 session_name() 將定義腳本將使用的實際 session 名稱。

因此,每個連結、表單、header() 和 Javascript 程式碼都會將 SESSION_NAME 值轉發到下一個腳本,並且它會知道必須使用哪個 session。如果沒有提供任何值,它會產生一個新的值(因此,為新的分頁建立一個新的 session)。

您可能會問為什麼不使用 cookie 來傳遞 SESSION_NAME 以及 session id。好吧,cookie 的問題是所有分頁都將共享相同的 cookie 來執行此操作,並且 session 無論如何都會混合。如果您將它們設定在不同的路徑中,並且每個 cookie 都可以在它們自己的目錄中使用,則 cookie 將部分起作用。但這不會使每個分頁中的 session 完全彼此分離。我認為透過 URL 使用 GET 和 POST 傳遞 session 名稱是最好的方法。
14
pautzomat at web dot de
21 年前
請注意,絕對 URL 不會自動被改寫以包含 SID。

當然,文件中有說明(「傳遞 Session ID」),而且有這個限制也完全合理,但這是我的遭遇:
我使用 session 一段時間都沒有問題。當我使用一個全域設定檔包含在我的所有腳本中時,它包含了一行像這樣的程式碼:

$sHomeDirectory = 'http://my.server.com/one/of/my/projects'

它被用來確保所有自動產生的連結都有正確的前綴(就像 phpMyAdmin 中的 $cfg['PmaAbsoluteUri'] 一樣)。在引入這個變數後,沒有任何連結會再傳遞 SID,導致每個腳本都回到登入頁面。我花了數小時(!!)才發現這不是我的程式碼中的錯誤,也不是 php.ini 中的設定錯誤,然後又花了一些時間才找出原因。我完全忘記了上述的限制(如果我曾經知道的話...)。

省略 'http:' 就解決了問題。

好吧,當然這是我自己的錯誤,但它只是顯示一個人可以多麼容易地在幾個小時內破壞自己的工作... 只是別這樣做 ;)
5
hinom - iMasters
16 年前
簡單的 session 測試

<?php
/* [danbrown AT php DOT net 的編輯:
這個筆記的作者在他的測試中將此
檔案命名為 tmp.php。如果
您將其另存為不同的名稱,
只需更新底部的連結以反映更改。] */

session_start();

$sessPath = ini_get('session.save_path');
$sessCookie = ini_get('session.cookie_path');
$sessName = ini_get('session.name');
$sessVar = 'foo';

echo
'<br>sessPath: ' . $sessPath;
echo
'<br>sessCookie: ' . $sessCookie;

echo
'<hr>';

if( !isset(
$_GET['p'] ) ){
// 實例化新的 session 變數
$_SESSION[$sessVar] = 'hello world';
}else{
if(
$_GET['p'] == 1 ){

// 列印 session 值和全域 cookie PHPSESSID
echo $sessVar . ': ';
if( isset(
$_SESSION[$sessVar] ) ){
echo
$_SESSION[$sessVar];
}else{
echo
'[不存在]';
}

echo
'<br>' . $sessName . ': ';

if( isset(
$_COOKIE[$sessName] ) ){
echo
$_COOKIE[$sessName];
}else{
if( isset(
$_REQUEST[$sessName] ) ){
echo
$_REQUEST[$sessName];
}else{
if( isset(
$_SERVER['HTTP_COOKIE'] ) ){
echo
$_SERVER['HTTP_COOKIE'];
}else{
echo
'問題,請檢查您的 PHP 設定';
}
}
}

}else{

// 使用 unset() 函數銷毀 session
unset( $_SESSION[$sessVar] );

// 檢查是否已銷毀
if( !isset( $_SESSION[$sessVar] ) ){
echo
'<br>';
echo
$sessName . ' 已被 "unset"';
}else{
echo
'<br>';
echo
$sessName . ' 未被 "unset"';
}

}
}
?>
<hr>
<a href=tmp.php?p=1>測試 1 (列印 session 值)</a>
<br>
<a href=tmp.php?p=2>測試 2 (終止 session)</a>
4
Csar
16 年前
Internet Explorer 中有一個錯誤,如果伺服器的名稱不是有效的名稱,session 將無法運作。例如...如果您的伺服器稱為 web_server(_ 不是有效的字元),如果您呼叫一個使用 session 的頁面,如 http://web_server/example.php,您的 session 將無法運作,但如果您像這樣呼叫腳本,session 將會運作:
[IP 位址]/example.php
4
Sam Yong - hellclanner at live dot com
13 年前
以下已在 PHP 5.3.5 中測試為真。

在腳本執行後,即在 __destruct 函數中設定 session 變數,將不會生效。

<?php

class Example{

function
__destruct(){
$_SESSION['test'] = true;
session_write_close();
}

}

?>

上面的範例不會將任何內容寫入暫存 session 檔案,正如我透過自訂 Session Save Handler 觀察到的。
4
Jeremy Speer
14 年前
在處理一個專案時,我發現需要在兩個不同的軟體之間切換 live session。這樣做的文件散落在不同的網站上,特別是在評論區而不是範例中。我遇到的一個困難是,其中一個應用程式的 session save handler 已設定,而另一個則沒有。現在,我沒有在 function session_set_save_handler() 中編寫程式碼,而是在我完成函數後(手動)才使用它,但是這個函數可以很容易地擴展以包含該功能。基本上,它只是覆蓋系統的預設 session save handler。為了克服這一點,在您使用 getSessionData() 後,只需使用適當的值呼叫 session_write_close()、session_set_save_handler(),然後使用它們的適當值重新執行 session_name()、session_id() 和 session_start()。如果您不知道 session ID,它是在 $_COOKIE[session_name] 或 $_REQUEST[session_name] 中的字串,如果您正在使用 trans_sid。[注意:使用來自 $_REQUEST 的資料時要小心,如果可能的話,請改用 $_GET 或 $_POST,具體取決於頁面]。

<?php
function getSessionData ($session_name = 'PHPSESSID', $session_save_handler = 'files') {
$session_data = array();
# 我們是否被告知舊的 session ID 是什麼?沒有該資訊,我們無法繼續它
if (array_key_exists($session_name, $_COOKIE)) {
# 儲存目前的 session ID
$session_id = $_COOKIE[$session_name];
$old_session_id = session_id();

# 寫入並關閉目前的 session
session_write_close();

# 取得舊的 save handler,並切換到 files
$old_session_save_handler = ini_get('session.save_handler');
ini_set('session.save_handler', $session_save_handler);

# 現在我們可以切換 session,並擷取舊的 session 名稱
$old_session_name = session_name($session_name);
session_id($session_id);
session_start();

# 取得所需的 session 資料
$session_data = $_SESSION;

# 關閉此 session,切換回原始的 handler,然後重新啟動舊的 session
session_write_close();
ini_set('session.save_handler', $old_session_save_handler);
session_name($old_session_name);
session_id($old_session_id);
session_start();
}

# 現在返回我們剛才檢索的資料
return $session_data;
}
?>
1
hinom06 [at] hotmail.co.jp
14 年前
簡單的 session 測試版本 1.1

<?php
/* [由 danbrown AT php DOT net 編輯:
此註解的作者在他的測試中將此
檔案命名為 tmp.php。如果您將它儲存為不同的名稱,
請簡單更新底部的連結以反映變更。] */

error_reporting( E_ALL );
ini_set( 'display_errors', 1);
date_default_timezone_set('Asia/Tokyo');

//ini_set( 'session.save_path', '/tmp' ); // 用於除錯目的

session_start();

// 檢查 session_id() 是否存在。
/*
例如,如果存在且 session 無法讀取,則必須在 URL 中將 session.name 作為參數傳送。

有些伺服器設定可能無法辨識 PHPSESSID,即使 transid 值為 0 或 1。
因此,此測試有助於找出任何原因。

*/
if( session_id() == '' )
{
echo
'session_id() 為空';
}else{
echo
session_id();
}
echo
'<hr>';

$sessPath = ini_get('session.save_path');
$sessCookie = ini_get('session.cookie_path');
$sessName = ini_get('session.name');
$sessVar = 'foo';

echo
'<br>sessPath: ' . $sessPath;
echo
'<br>sessCookie: ' . $sessCookie;

echo
'<hr>';

if( !isset(
$_GET['p'] ) ){
// 實例化新的 session 變數
$_SESSION[$sessVar] = 'hello world';
}else{
if(
$_GET['p'] == 1 ){

// 列印 session 值和全域 cookie PHPSESSID
echo $sessVar . ': ';
if( isset(
$_SESSION[$sessVar] ) ){
echo
$_SESSION[$sessVar];
}else{
echo
'[不存在]';
}

echo
'<br>' . $sessName . ': ';

if( isset(
$_COOKIE[$sessName] ) ){
echo
$_COOKIE[$sessName];
}else{
if( isset(
$_REQUEST[$sessName] ) ){
echo
$_REQUEST[$sessName];
}else{
if( isset(
$_SERVER['HTTP_COOKIE'] ) ){
echo
$_SERVER['HTTP_COOKIE'];
}else{
echo
'發生問題,請檢查您的 PHP 設定';
}
}
}

}else{

// 使用 unset() 函式銷毀 session
unset( $_SESSION[$sessVar] );

// 檢查是否已銷毀
if( !isset( $_SESSION[$sessVar] ) ){
echo
'<br>';
echo
$sessName . ' 已 "unseted"';
}else{
echo
'<br>';
echo
$sessName . ' 未 "unseted"';
}

}
}
?>
<hr>
<a href=tmp.php?p=1&<?php echo $sessName . '=' . session_id();?>>測試 1 (列印 session 值)</a>
<br>
<a href=tmp.php?p=2&<?php echo $sessName . '=' . session_id();?>>測試 2 (終止 session)</a>
0
brfelipe08 at hotmail dot com
15 年前
如果您需要使用 session,且某些以拉丁語系為基礎的語言需要一些重音符號,您應該以 ISO-8859-1 編碼您的檔案。
如果您嘗試使用 UTF-8 (無論是否帶有 BOM),您會遇到一些問題,而且 ANSI 不支援重音符號。
ISO-8859-1 將同時支援 session 和重音符號。
-1
carl /a/ suchideas /o/ com
17 年前
這個清單中要新增的另一個陷阱是,使用相對的 session.save_path 是非常糟糕的主意。

如果您非常小心,幾乎可以順利完成,但請注意兩個相關的重點

1) 路徑是相對於最初執行的指令碼目錄而取得,因此除非所有頁面都從相同的目錄執行,否則您必須在每個個別的子資料夾中個別設定目錄

2) 如果您呼叫某些函式,例如 session_regenerate_id(),PHP 將嘗試取得相對於可執行檔的 session 目錄,或類似的東西,在可執行檔中建立錯誤。這會提供稍微隱晦的錯誤訊息,如下所示

Warning: Unknown: open(relative_path\ilti9oq3j9ks0jvih1fmiq4sv1.session, O_RDWR) failed: No such file or directory (2) in Unknown on line 0

Warning: Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correct (relative_path) in Unknown on line 0

... 所以別再費心了。直接使用

<?php ini_set("session.save_path",dirname(__FILE__)."/relative_path"); ?>

(或等效方式) 在您知道始終在相對於檔案的相同位置的檔案中。

{PHP 版本 5.1.6}
-1
LaurentT
16 年前
針對 UNIX

在同一部伺服器上擁有不同網站時,可能會遇到一些 session 問題:如果一次使用多個網站,session 會合併;如果網站由不同的系統使用者擁有,session 會當機。例如

www.example.com 儲存在 /home/site1/www
www.example.net 儲存在 /home/site2/www

同時使用 www.example.com 和 www.example.net 會導致 session 行為異常。

如果您使用 PHP 作為 Apache 模組,您可以輕鬆地在 http.conf 中使用 php_value,根據網站設定唯一的 session.name。但是,如果您使用 suPHP (PHP 作為 CGI),則無法使用 php_value,但可以使用 suPHP_ConfigPath。

以下是一個範例

<VirtualHost 10.10.10.10:8081>
DocumentRoot /home/site1/www
ServerName www.example.com
suPHP_ConfigPath /home/site1/server_config
</VirtualHost>
<VirtualHost 10.10.10.10:8082>
DocumentRoot /home/site2/www
ServerName www.example.net
suPHP_ConfigPath /home/site2/server_config
</VirtualHost>

每個 server_config 資料夾都包含特定於 vHost 的 php.ini 檔案。然後您只需要將每個 session.name 的值變更為唯一的值,就完成了!
-1
Nigel Barlass
17 年前
我必須為我的 PHP 版本修改 Lima 關於 session 和瀏覽器索引標籤的註解,因為呼叫 uniqid('') 將會傳回字母數字字串。
因此,ereg 語句應該是
if(!ereg('^SESS[0-9a-z]+$',$_REQUEST['SESSION_NAME'])) {...
-1
paul at shirron dot net
16 年前
在 php.ini 中,我有

session.save_path="C:\DOCUME~1\pjs9486\LOCALS~1\Temp\php\session"

我當時在清除暫存目錄,並刪除了 php 目錄。Session 功能停止運作。我重新建立了 php 目錄。仍然沒用。我在 php 目錄中重新建立了 session 目錄,session 功能才恢復運作。

我原本預期 session_start() 會在路徑中重新建立目錄 (如果它們不存在),但是它並沒有這麼做。

提醒自己:別再這樣做了!!!!
-2
ted at tedmurph dot com
14 年前
我當時在 $_SESSION 資訊的寫入或以看似隨機的方式遺失時遇到問題。在 Zend OAuth 模組的深處有一個 Location: 呼叫正在進行,我正在使用帶有 PHP 作為 CGI 的 IIS 伺服器等等。

答案很簡單,您需要讓網域保持一致,session 才能一致地運作。在我的案例中,我當時在 www.EXAMPLE.com:888 和 EXAMPLE.com:888 之間來回切換。不尋常的連接埠、隱藏的 Location: 呼叫、與 OAuth 的交接等等,都讓我感到困惑,但間歇性的錯誤是由於這個保持網域一致的簡單疏忽所造成。
-2
farhad dot pd at gmail dot com
7 年前
<?php
class Session
{

public static
$seesionFlashName = '__FlashBack';
/**
* [__construct description]
*/
public function __construct() {

}

public static function
start() {
ini_set('session.use_only_cookies', 'Off');
ini_set('session.use_cookies', 'On');
ini_set('session.use_trans_sid', 'Off');
ini_set('session.cookie_httponly', 'On');

if (isset(
$_COOKIE[session_name()]) && !preg_match('/^[a-zA-Z0-9,\-]{22,52}$/', $_COOKIE[session_name()])) {
exit(
'Error: Invalid session ID!');
}

session_set_cookie_params(0, '/');
session_start();
}

public static function
id() {
return
sha1(session_id());
}

public static function
regenerate() {
session_regenerate_id(true);
}

/**
* [exists description]
* @param [type] $name [description]
* @return [type] [description]
*/
public static function exists($name) {
if(isset(
$name) && $name != '') {
if(isset(
$_SESSION[$name])) {
return
true;
}
}

return
false;
}

/**
* [set description]
* @param [type] $name [description]
* @param [type] $value [description]
*/
public static function set($name='', $value='') {
if(
$name != '' && $value != '') {
$_SESSION[$name] = $value;
}
}

/**
* [get description]
* @param [type] $name [description]
* @return [type] [description]
*/
public static function get($name) {
if(
self::exists($name)) {
return
$_SESSION[$name];
}

return
false;
}

/**
* [delete description]
* @param [type] $name [description]
* @return [type] [description]
*/
public static function delete($name) {
if(
self::exists($name)) {
unset(
$_SESSION[$name]);
}

return
false;
}

/**
* [setFlash description]
* @param string $value [description]
*/
public static function setFlash($value='') {
if(
$value != '') {
self::set(self::$seesionFlashName, $value);
}
}

/**
* [getFlash description]
* @return [type] [description]
*/
public static function getFlash() {
if(
self::exists(self::$seesionFlashName)) {
ob_start();
echo
self::get(self::$seesionFlashName);
$content = ob_get_contents();
ob_end_clean();

self::delete(self::$seesionFlashName);

return
$content;
}

return
false;
}

/**
* [flashExists description]
* @return [type] [description]
*/
public static function flashExists() {
return
self::exists(self::$seesionFlashName);
}

/**
* [destroy description]
* @return void [description]
*/
public static function destroy() {
foreach(
$_SESSION as $sessionName) {
self::delete($sessionName);
}

session_destroy();
}
}
?>

伊朗 PHP 開發人員
Farhad Zand Moghadam
-2
edA-qa at disemia dot com
15 年前
Debian 使用者請注意。為了讓您徹底崩潰,Debian 有自己的一套 Session 管理方式,而且會完全忽略您在 PHP 腳本中所做的任何修改。

Debian 設定了一個 <crontab /etc/cron.d/php5>,它會刪除所有檔案(包括子目錄中的檔案),這些檔案超過 <php.ini> 檔案中指定的 gc_maxlifetime 值。

也就是說,在 Debian(以及像 Ubuntu 這類變體)上,修改 Session 過期設定(例如 gc_maxlifetime)是*沒有用的*。您*必須*修改全域的 <php.ini>。即使是 <.htaccess> 檔案也幫不了您。
-3
Trevor Brown
14 年前
確認行為,以防萬一這能幫其他人節省一些時間...

當 session.gc_probability = 0 且 session.gc_divisor = 0 時,(可能)會出現特殊情況。根據他們在底層想要如何運算,程式設計師*可能*會選擇將 0/0 解釋為 1(在某些數學證明中,有時會假設如此),但幸好他們似乎沒有這樣做。當兩個值都設為 0 時,Session 程式碼會遵循指令的精神,以零機率呼叫垃圾收集器。至少在 php 5.2.5 中是這樣(為了確定,我檢查了原始碼)。

但是,為了安全起見,將兩者都設為零*可能*不是一個好主意,因為通常 0/0 是未定義的,所以它可能意味著任何事情(有些論點聲稱 0/0 等於每個分數)。您難道不希望確定您的機率設定為多少嗎?換句話說,不要設定 gc_divisor = 0
-3
session a emailaddress d cjb d net
16 年前
在文件中或任何人的評論中都沒有提到,但將 session.gc_maxlifetime 設定為 0 表示 Session 在瀏覽器關閉之前不會過期。

當然,這仍然無法解決與垃圾收集器自行其是相關的問題。
解決這個問題的最佳方法似乎仍然是變更 session.save_path
-5
jitchavan at gmail dot com
13 年前
IE 問題:-

當表單目標設定為 iframe 來源,並在發佈表單內容後設定 Session 變數時,
在這種情況下,如果父頁面的圖片來源為空白,則在 iframe 動作頁面中設定的 Session 值將會意外地遺失,僅在 IE 中如此。解決方案很簡單,不要讓圖片來源為空白。
-5
edA-qa at disemia dot com
15 年前
Session 可能會在您在 gc_maxlifetime 中設定的時間限制之前被刪除。

如果您在同一伺服器上有多個頁面,每個頁面都使用 Session(相同或不同名稱的 Session,這都無所謂),則任何這些腳本的 *最小* gc_maxlifetime 最終都會成為 Session 檔案的有效生命週期。

這也可能適用於從該機器上的 PHP 腳本的 CLI 呼叫中產生的生命週期,該腳本恰好使用 Session。

這可能會很麻煩,因為即使您認為您的所有頁面都包含設定腳本的相同檔案,但即使是單一的 PHP 頁面,沒有該檔案也可能呼叫 GC 並刪除腳本。

因此,如果您需要設定較長的 gc_maxlifetime,您最好透過 INI 檔案或整個目錄的 .htaccess 檔案來執行此操作。
-4
shanemayer42 at yahoo dot com
24 年前
工作階段垃圾回收觀察

看來工作階段檔案的垃圾回收發生在目前工作階段載入「之後」。

這表示
即使 session.gc_maxlifetime = 1 秒,
如果有人啟動一個工作階段 A,然後一小時內都沒有人啟動工作階段,那個人可以重新連線到工作階段 A,並且他們先前工作階段的所有值都會是可用的(也就是說,即使工作階段 A 比 gc_maxlifetime 還要舊,也不會被清除)。
To Top