PHP 日本研討會 2024

session_start

(PHP 4、PHP 5、PHP 7、PHP 8)

session_start啟動新的或恢復現有的 session

描述

session_start(array $options = []): bool

session_start() 建立一個 session,或根據透過 GET 或 POST 請求傳遞的 session 識別符,或透過 cookie 傳遞的 session 識別符,恢復目前的 session。

當呼叫 session_start() 或 session 自動啟動時,PHP 將會呼叫 open 和 read session 儲存處理程式。這些將會是預設提供或由 PHP 擴充功能(例如 SQLite 或 Memcached)提供的內建儲存處理程式;或者可以是 session_set_save_handler() 定義的自訂處理程式。read 回呼將會擷取任何現有的 session 資料(以特殊的序列化格式儲存),並且在 read 回呼將儲存的 session 資料回傳給 PHP session 處理時,將會取消序列化並用於自動填入 $_SESSION 超級全域變數。

若要使用已命名的 session,請在呼叫 session_start() 之前呼叫 session_name()

當啟用 session.use_trans_sid 時,session_start() 函式將會註冊用於 URL 重寫的內部輸出處理程式。

如果使用者將 ob_gzhandler 或類似項目與 ob_start() 搭配使用,則函式順序對於正確輸出非常重要。例如,必須在啟動 session 之前註冊 ob_gzhandler

參數

options

如果提供,這是一個選項的關聯陣列,將覆寫目前設定的 session 配置指令。金鑰不應包含 session. 前綴。

除了正常設定的配置指令之外,還可以提供 read_and_close 選項。如果設定為 true,這將導致 session 在讀取後立即關閉,從而避免在 session 資料不會變更時進行不必要的鎖定。

回傳值

如果成功啟動 session,此函式會回傳 true,否則回傳 false

變更記錄

版本 描述
7.1.0 session_start() 無法啟動 session 時,現在會回傳 false,並且不再初始化 $_SESSION

範例

基本 session 範例

範例 #1 page1.php

<?php
// page1.php

session_start();

echo
'歡迎來到第 1 頁';

$_SESSION['favcolor'] = 'green';
$_SESSION['animal'] = 'cat';
$_SESSION['time'] = time();

// 如果 session cookie 被接受,則運作
echo '<br /><a href="page2.php">第 2 頁</a>';

// 或者,如果需要,可以傳遞 session id
echo '<br /><a href="page2.php?' . SID . '">第 2 頁</a>';
?>

在檢視 page1.php 之後,第二頁 page2.php 將會神奇地包含 session 資料。請閱讀 session 參考,以取得有關 傳播 session id 的資訊,例如,其中說明常數 SID 的用途。

範例 #2 page2.php

<?php
// page2.php

session_start();

echo
'歡迎來到第 2 頁<br />';

echo
$_SESSION['favcolor']; // green
echo $_SESSION['animal']; // cat
echo date('Y m d H:i:s', $_SESSION['time']);

// 您可能想在這裡使用 SID,就像我們在 page1.php 中所做的一樣
echo '<br /><a href="page1.php">第 1 頁</a>';
?>

提供 session_start() 的選項

範例 #3 覆寫 cookie 的存留時間

<?php
// 這會傳送一個持續一天的永久 cookie。
session_start([
'cookie_lifetime' => 86400,
]);
?>

範例 #4 讀取 session 並將其關閉

<?php
// 如果我們知道不需要變更 session 中的任何內容,
// 我們可以立即讀取並關閉,以避免
// 鎖定 session 檔案並封鎖其他頁面
session_start([
'cookie_lifetime' => 86400,
'read_and_close' => true,
]);

注意事項

注意:

若要使用以 cookie 為基礎的 session,必須在向瀏覽器輸出任何內容之前呼叫 session_start()

注意:

建議使用 zlib.output_compression,而非 ob_gzhandler()

注意:

此函式會根據配置傳送數個 HTTP 標頭。請參閱 session_cache_limiter() 以自訂這些標頭。

參見

新增附註

使用者提供的附註 36 則附註

30
linblow at hotmail dot fr
13 年前
如果您想要使用類別來處理 session,我寫了這個小類別

<?php

/*
使用靜態方法 getInstance 來取得物件。
*/

class Session
{
const
SESSION_STARTED = TRUE;
const
SESSION_NOT_STARTED = FALSE;

// Session 的狀態
private $sessionState = self::SESSION_NOT_STARTED;

// 類別的唯一實例
private static $instance;


private function
__construct() {}


/**
* 傳回 'Session' 的唯一實例。
* 如果 session 尚未初始化,則會自動初始化。
*
* @return object
**/

public static function getInstance()
{
if ( !isset(
self::$instance))
{
self::$instance = new self;
}

self::$instance->startSession();

return
self::$instance;
}


/**
* (重新)啟動 session。
*
* @return bool 如果 session 已初始化則傳回 TRUE,否則傳回 FALSE。
**/

public function startSession()
{
if (
$this->sessionState == self::SESSION_NOT_STARTED )
{
$this->sessionState = session_start();
}

return
$this->sessionState;
}


/**
* 在 session 中儲存資料。
* 範例:$instance->foo = 'bar';
*
* @param name 資料的名稱。
* @param value 您的資料。
* @return void
**/

public function __set( $name , $value )
{
$_SESSION[$name] = $value;
}


/**
* 從 session 中取得資料。
* 範例:echo $instance->foo;
*
* @param name 要取得的資料名稱。
* @return mixed 儲存在 session 中的資料。
**/

public function __get( $name )
{
if ( isset(
$_SESSION[$name]))
{
return
$_SESSION[$name];
}
}


public function
__isset( $name )
{
return isset(
$_SESSION[$name]);
}


public function
__unset( $name )
{
unset(
$_SESSION[$name] );
}


/**
* 銷毀目前的 session。
*
* @return bool 如果 session 已刪除則傳回 TRUE,否則傳回 FALSE。
**/

public function destroy()
{
if (
$this->sessionState == self::SESSION_STARTED )
{
$this->sessionState = !session_destroy();
unset(
$_SESSION );

return !
$this->sessionState;
}

return
FALSE;
}
}

/*
範例:
*/

// 我們取得實例
$data = Session::getInstance();

// 讓我們在 session 中儲存資料
$data->nickname = '某人';
$data->age = 18;

// 讓我們顯示資料
printf( '<p>我的名字是 %s,我 %d 歲。</p>' , $data->nickname , $data->age );

/*
它將會顯示:

Array
(
[nickname] => 某人
[age] => 18
)
*/

printf( '<pre>%s</pre>' , print_r( $_SESSION , TRUE ));

// TRUE
var_dump( isset( $data->nickname ));

// 我們銷毀 session
$data->destroy();

// FALSE
var_dump( isset( $data->nickname ));

?>

我比較喜歡使用這個類別,而不是直接使用 $_SESSION 陣列。
21
aaronw at catalyst dot net dot nz
9 年前
如同其他人所指出的,PHP 的 session 處理器是阻塞的。當你的其中一個腳本呼叫 session_start() 時,任何其他使用相同 session ID 呼叫 session_start() 的腳本都會休眠,直到第一個腳本關閉 session。

一個常見的解決方法是,每次你想更新 session 時都呼叫 session_start() 和 session_write_close()。

這樣做的問題是,每次你呼叫 session_start() 時,PHP 都會在 HTTP 回應標頭中列印一份重複的 session cookie。如果你這樣做足夠多次(在一個長時間運行的腳本中可能如此),回應標頭可能會變得非常大,以至於導致網頁伺服器和瀏覽器崩潰或拒絕你的回應,認為它是格式錯誤的。

這個錯誤已回報給 PHP 總部,但他們將其標記為「不會修復」,因為他們說你不應該像這樣在單個腳本中開啟和關閉 session。https://bugs.php.net/bug.php?id=31455

作為一個解決方案,我寫了一個函式,使用 headers_list() 和 header_remove() 來清除重複的 cookie。有趣的是,即使在 PHP 發送重複的 session cookie 的請求中,headers_list() 仍然只列出一個 session cookie 的副本。儘管如此,呼叫 header_remove() 仍然會移除所有重複的副本。

<?php
/**
* 每次你呼叫 session_start() 時,PHP 都會在回應標頭中新增另一個
* 相同的 session cookie。這樣做足夠多次,你的回應標頭就會變得夠大
* 以至於讓網頁伺服器窒息。
*
* 這個方法會清除重複的 session cookie。你可以在每次你呼叫 session_start() 後呼叫它,或是在你發送標頭之前呼叫它。
*/
function clear_duplicate_cookies() {
// 如果標頭已經發送,我們就無能為力了
if (headers_sent()) {
return;
}

$cookies = array();
foreach (
headers_list() as $header) {
// 識別 cookie 標頭
if (strpos($header, 'Set-Cookie:') === 0) {
$cookies[] = $header;
}
}
// 移除所有 cookie 標頭,包括重複的標頭
header_remove('Set-Cookie');

// 還原每個 cookie 的一個副本
foreach(array_unique($cookies) as $cookie) {
header($cookie, false);
}
}
?>
16
ohcc at 163 dot com
10 年前
如果 php ini 檔案中的 session.use_trans_sid 指令設定為 0,則常數 SID 將始終為 ''(空字串)。

所以在你的 php 腳本中使用 SID 之前,請記得將 session.use_trans_sid 設定為 1 並重新啟動你的伺服器。
10
marco dot agnoli at me dot com
7 年前
我最近做了一個有趣的觀察

看來,即使 session 沒有正確建立,`session_start()` 也可能會傳回 `true`。在我的情況下,磁碟儲存空間已滿,因此 session 資料無法寫入磁碟。當 session 沒有寫入磁碟時,我有一些邏輯會導致無限迴圈。

為了檢查 session 是否真的已儲存到磁碟,我使用了

```
<?php

function safe_session_start() {
# 嘗試啟動一個 session
if (!@\session_start()) return false;

#
# 檢查是否需要執行
# 寫入測試。
#
if (!isset($_SESSION['__validated'])) {
$_SESSION['__validated'] = 1;

# 嘗試將 session 寫入磁碟
@\session_write_close();

# 從記憶體中取消設定該變數。
# 此步驟可能是不必要的
unset($_SESSION['__validated']);

# 重新啟動 session
@\session_start();

# 檢查變數值是否保留
if (!isset($_SESSION['__validated'])) {
# Session 沒有寫入磁碟
return false;
}
}

return
true;
}

if (!
safe_session_start()) {
# Session 可能沒有寫入磁碟...
# 請適當處理錯誤。
}

?>


我花了一段時間才弄清楚這個。

也許對某些人有幫助!
11
bachtel at [googles email service]dotcom
7 年前
如果您透過 session_set_save_handler() 使用自訂的 session 處理器,在 PHP 7.1 中呼叫 session_start() 時,您可能會看到像這樣的錯誤
session_start(): Failed to read session data: user (path: /var/lib/php/session) in ...

截至撰寫本文時,這似乎發生在 PHP 7.1 中,而在 PHP 7.0 中看起來沒問題。

這也很難追蹤,因為如果這個 id 已經存在一個 session(可能是由較早版本的 PHP 建立的),它就不會觸發這個問題,因為 $session_data 不會是 null。

修復方法很簡單...您只需要在您的讀取函式中檢查 'null'

<?php

function read($id)
{
//... 從資料庫、磁碟、memcache 等中提取資料
$session_data = getSessionDataFromSomewhere($id);

//在返回之前檢查 $session_data 是否為 null (至關重要)
if(is_null($session_data))
{
$session_data = ''; //使用空字串而不是 null!
}

return
$session_data;
}

?>
9
emre@yazici
15 年前
PHP 手冊特別指出這個常見的錯誤

根據 session 處理器的不同,並非所有字元都允許在 session id 中使用。例如,檔案 session 處理器僅允許 a-z A-Z 0-9 ,(逗號)和 -(減號)範圍內的字元!

請參閱 session_id() 手冊頁以取得更多詳細資訊。
1
bwz
1 年前
請注意另一個關於封鎖 session 的問題:如果您想呼叫需要使用相同 session 存取您網站的外部程式(或使用外部服務)。

例如,我正在將一個頁面列印為 PDF。我可以直接將網頁儲存為 HTML 檔案。但是 HTML 中的圖片也是私有的,需要目前的使用者 session 才能看到。

會發生的情況是,這個程式可能會無限期地掛起(或逾時),因為 session_start 會等待父 PHP 程序釋放鎖定。而且 session_start 不會遵守 max_execution_time(如這個錯誤中記錄的:https://bugs.php.net/bug.php?id=72345),因此這會在幾個請求後有效地殺死伺服器,因為每個請求都會永遠掛起

如果您使用外部 HTTP 服務,情況也是一樣

<?php
$pdf
= file_get_contents('http://pdf.website.tld/?url=http://website.tld/print.php');
?>

該服務會等待網站主機釋放鎖定,但由於它正在等待 PDF 服務完成,因此無法釋放...

不錯的解決方案是在 session_start 之後立即呼叫 session_write_close 來釋放鎖定,當您需要寫入 session 時,再次執行相同的操作,但正如所指出的,它也有自己的問題。使用自訂的 session 處理器可能是最佳解決方案。
8
elitescripts2000 at yahoo dot com
10 年前
關於 AJAX 應用程式中 Session 的 3 個簡單但至關重要的事項。

<?php
// session 啟動

// 如果使用整個網域,包含句點 (.) 非常重要 (.yourdomain.com)
// 設定您的 session 將始終運作的根路徑非常重要... (/members) 將確保 session 不會被路徑為 (/admin) 的 session 干擾... 因此您可以登入為 /admin 和 /members... 永遠不要執行 unset($_SESSION)
// $_SESSION=array(); 是首選,session_unset(); session_destroy();

session_set_cookie_params(0, '/members', '.yourdomain.com', 0, 1);
session_start();
$_SESSION = array();
session_unset();
session_destroy();

session_set_cookie_params(0, '/members', '.yourdomain.com', 0, 1);
session_start();

$_SESSION['whatever'] = 'youwhat';

// session 銷毀

// 為了安全起見,請清除您的 $_SESSION 陣列
// 接下來,大多數人沒有做的是刪除 session cookie!
// 透過在目前時間很久之前過期 cookie,可以輕鬆刪除 cookie。
// 刪除 cookie 的唯一方法是確保所有參數都與要刪除的 cookie 相符...這很容易使用 session_get_cookie_params() 取得這些參數...
// 最後,請依此順序使用 session_unset(); 和 session_destroy();,以確保 Chrome、IE、Firefox 和其他瀏覽器正確地銷毀 session。
$_SESSION = array();
if (
ini_get('session.use_cookies'))
{
$p = session_get_cookie_params();
setcookie(session_name(), '', time() - 31536000, $p['path'], $p['domain'], $p['secure'], $p['httponly']);
}
session_unset();
session_destroy();

// AJAX 和 SESSIONS。
// 範例... 您啟動一個基於 session 的 PHP 頁面,然後使用相同的 SESSION 呼叫 Ajax (XMLHTTP) 進行驗證,以輪詢和輸出資料,舉例來說。但是,您會注意到當您嘗試啟動輪詢 AJAX 呼叫時,它始終會掛起,並且似乎會掛在 session_start() 上。
// 這是因為 session 在第一個頁面中開啟,呼叫 AJAX 輪詢範例,並嘗試開啟相同的 session (用於驗證) 並執行 AJAX 呼叫,您必須呼叫 session_write_close(); 這表示您已完成寫入 $_SESSION 變數,它實際上代表一個必須使用 session_write_close(); 關閉的檔案....
// 之後您可以呼叫您的 AJAX 輪詢程式碼,以重新開啟相同的 session 並執行其輪詢...
// 通常,當指令碼關閉或執行完成時,$_SESSION 會自動關閉
// 因此,如果您需要在開啟 SESSION 後讓 PHP 頁面保持執行,只需在完成寫入 $_SESSION 後關閉它,以便 AJAX 輪詢頁面可以驗證並在單獨的網頁中使用相同的 session...

session_write_close();

?>

希望這能幫助某些人處理他們的 session...
謝謝。
10
dave1010 at gmail dot com
13 年前
PHP 會鎖定 session 檔案,直到它關閉。如果您有 2 個指令碼使用相同的 session(即來自同一使用者),那麼第 2 個指令碼將無法完成對 session_start() 的呼叫,直到第一個指令碼執行完成。

如果您有執行超過一秒的指令碼,並且使用者可能會同時發出多個請求,那麼在您完成寫入 session 資料後立即呼叫 session_write_close() 是值得的。

<?php
// 鎖定放置在 session 上,因此其他指令碼必須等待
session_start();

// 執行所有寫入 $_SESSION 的操作
$_SESSION['a'] = 1;

// $_SESSION 仍然可以讀取,但寫入不會更新 session。
// 鎖定已移除,其他指令碼現在可以讀取 session
session_write_close();

do_something_slow();
?>

http://konrness.com/php5/how-to-prevent-blocking-php-requests/ 發現的
4
Anonymous
4 年前
請注意 'read_and_close' 選項。與預設的 PHP 行為不同,當您不關閉 session(或明確使用 session_write_close 時),它不會更新 session 檔案的最後修改時間。
舊的 session 檔案(對我來說,是超過 24 分鐘的檔案)會偶爾被垃圾回收機制清除(對我來說,是每小時的 09 分和 39 分)。
所以,即使頁面定期向伺服器發送只讀取並關閉 session 的請求,session 仍可能會消失。
3
ben dot morin at spaboom dot com
18 年前
James 在 skinsupport dot com 提出了關於瀏覽器額外請求的好觀點(警告)。對於 favicon.ico 的請求,取決於它的處理方式,可能會對您的 session 產生意想不到的結果。

例如,假設您設定了 ErrorDocument 404 /signin.php,沒有 favicon.ico 檔案,並且您網站上所有使用者登入的頁面也都會在未登入時重新導向到 /signin.php。

如果 signin.php 執行任何清理或重新指派 session_id 的操作(所有好的 signin.php 頁面都應該這樣做),那麼瀏覽器發送的 favicon.ico 額外請求可能會破壞由實際請求設定的 session。

感謝 James 指出這一點,以及我沒有看清楚並發現它如何應用於我的問題。也感謝 Firefox Live HTTP Headers 擴充功能顯示了額外的請求。

如果您的 session cookie 沒有被發送,或者 session 資料不是您預期的那樣,請不要在這上面浪費數天甚至數小時。至少,排除這種情況,看看是否有任何額外的請求可能是罪魁禍首。
9
someOne_01 at somewhere dot com
12 年前
當您有一個執行時間很長的匯入腳本時,瀏覽器似乎會鎖定,您將無法再訪問網站。這是因為有一個請求正在讀取並鎖定 session 檔案,以防止損壞。

您可以選擇:
- 使用 session_set_save_handler() 設定不同的 session 處理器
- 在匯入腳本中,當您不再需要 session 時就使用 session_write_close()(最好的時機是在長時間執行部分開始之前),如果您的匯入腳本需要更改 session 變數,您可以隨時多次使用 session_start。

範例
<?php
session_start
(); //初始化/開啟 session
$_SESSION['count'] = 0; // 在 session 中儲存一些東西
session_write_close(); //現在關閉它,
# 從這裡開始,可以執行其他所有腳本(使其看起來像多工)
for($i=0; $i<=100; $i++){ //執行 100 次循環
session_start(); //再次開啟 session 以編輯變數
$_SESSION['count'] += 1; //更改變數
session_write_close(); //再次關閉 session!
sleep(2); //每個循環睡眠兩秒,或執行繁重的工作
}
?>
4
James
18 年前
為了避免 PHP 自 4.3.3 版本以來,當您重複啟動 session 時發出的通知,請先檢查 session_id()

if (session_id() == "")
session_start();
5
jamestrowbridge at gmail dot com
14 年前
很遺憾,在我絞盡腦汁試圖找出為什麼我的應用程式在除了 IE(Internet Explorer)之外的所有瀏覽器(Opera、Chrome、Firefox、Safari,這是我測試過的)中都運行正常的原因後,當使用 DNS CNAME 記錄(像是與 DNS A 記錄不同的虛名,而 DNS A 記錄是伺服器的主機名稱)時,session 將無法正常運作。

如果在 CNAME 上儲存 session 變數
vanity.example.com,而伺服器的主機名稱是 hosname.example.com
然後嘗試從不同的頁面呼叫該變數,它將無法找到該變數,因為 CNAME 的關係(我猜測它將變數儲存在主機名稱下,然後在嘗試讀取它時,它仍然在 CNAME 下尋找)。當直接使用主機名稱訪問時,相同的應用程式可以正常運作。請記住,我是在內部網路上測試這個。
3
hu60 dot cn at gmail dot com
5 年前
以下程式碼顯示了 PHP session 的工作方式。函數 my_session_start() 的作用與 session_start() 幾乎相同。

<?php
error_reporting
(E_ALL);
ini_set('display_errors', true);
ini_set('session.save_path', __DIR__);

my_session_start();

echo
'<p>session id: '.my_session_id().'</p>';

echo
'<code><pre>';
var_dump($_SESSION);
echo
'</pre></code>';

$now = date('H:i:s');
if (isset(
$_SESSION['last_visit_time'])) {
echo
'<p>Last Visit Time: '.$_SESSION['last_visit_time'].'</p>';
}
echo
'<p>Current Time: '.$now.'</p>';

$_SESSION['last_visit_time'] = $now;

function
my_session_start() {
global
$phpsessid, $sessfile;

if (!isset(
$_COOKIE['PHPSESSID']) || empty($_COOKIE['PHPSESSID'])) {
$phpsessid = my_base32_encode(my_random_bytes(16));
setcookie('PHPSESSID', $phpsessid, ini_get('session.cookie_lifetime'), ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure'), ini_get('session.cookie_httponly'));
} else {
$phpsessid = substr(preg_replace('/[^a-z0-9]/', '', $_COOKIE['PHPSESSID']), 0, 26);
}

$sessfile = ini_get('session.save_path').'/sess_'.$phpsessid;
if (
is_file($sessfile)) {
$_SESSION = unserialize(file_get_contents($sessfile));
} else {
$_SESSION = array();
}
register_shutdown_function('my_session_save');
}

function
my_session_save() {
global
$sessfile;

file_put_contents($sessfile, serialize($_SESSION));
}

function
my_session_id() {
global
$phpsessid;
return
$phpsessid;
}

function
my_random_bytes($length) {
if (
function_exists('random_bytes')) {
return
random_bytes($length);
}
$randomString = '';
for (
$i = 0; $i < $length; $i++) {
$randomString .= chr(rand(0, 255));
}
return
$randomString;
}

function
my_base32_encode($input) {
$BASE32_ALPHABET = 'abcdefghijklmnopqrstuvwxyz234567';
$output = '';
$v = 0;
$vbits = 0;
for (
$i = 0, $j = strlen($input); $i < $j; $i++) {
$v <<= 8;
$v += ord($input[$i]);
$vbits += 8;
while (
$vbits >= 5) {
$vbits -= 5;
$output .= $BASE32_ALPHABET[$v >> $vbits];
$v &= ((1 << $vbits) - 1);
}
}
if (
$vbits > 0) {
$v <<= (5 - $vbits);
$output .= $BASE32_ALPHABET[$v];
}
return
$output;
}
4
jorrizza at gmail dot com
19 年前
如果你使用 JavaScript 的 window.open 開啟一個彈出視窗(請不要使用商業廣告彈窗!),IE 瀏覽器可能會阻擋 session cookie。
一個簡單的解決方法是在 GET 值中包含 session ID 來開啟新視窗。注意,我這裡沒有使用 SID,因為它並非總是可用。

----page.php----
//你必須在這裡啟用 session
window.open('popup.php?sid=<?php echo session_id(); ?>', '700x500', 'toolbar=no, status=no, scrollbars=yes, location=no, menubar=no, directories=no, width=700, height=500');

----popup.php----
<?php
session_id
(strip_tags($_GET['sid']));
session_start();
// 接著處理你的 session 變數
?>
3
axew3 at axew3 dot com
7 年前
X 維護者... 抱歉造成你們的困擾,請刪除這個重複的提交,因為我在一個瘋狂的「session」中提交的,我把瀏覽器分頁搞混了... 再次抱歉,alessio
https://php.dev.org.tw/manual/en/function.session-start.php#121310
3
schlang
15 年前
如果你的 session 儲存在資料庫中,請務必確保資料庫欄位的類型足夠大,可以容納你的 session 值。
3
andy_isherwood at hotmail dot com
16 年前
透過 session_start 建立的 session 僅適用於首次建立該 session 的頁面所在目錄樹內的頁面。

例如,如果首次建立 session 的頁面是 /dir1/dir2/index.php,然後使用者瀏覽到 dir2 以上的任何頁面(例如 /dir1/index.php),session_start 將會建立新的 session,而不是使用現有的 session。
2
ilnomedellaccount at gmail dot com
11 年前
關於 session_start()、自訂處理器和資料庫外鍵約束的一點說明,我認為這可能有些用處...

我們知道,如果我們希望將 session 儲存在資料庫表格中(而不是預設的儲存方式),我們可以參考 session_set_save_handler(...) 來達成。請注意,session_set_save_handler 必須(顯然地)在 session_start() 之前呼叫,但先讓我回到重點...

在呼叫 session_start() 的「第一次」,當 session 尚未存在時,PHP 會產生一個新的 session,但直到腳本執行結束才會呼叫寫入處理器。

因此,此時 session 存在於伺服器進程記憶體中,但在腳本結束之前不會在資料庫中顯示為一列。

這似乎是合理的,因為這避免了一些不必要的資料庫存取和資源使用,在我們甚至還沒用有意義和明確的資料填充 session 之前,但這也有副作用。

在我的情況下,腳本呼叫了 session_start() 以確保已啟動 session,然後使用 session_id() 來填充資料庫中的另一個表格,該表格具有到「sessions」表格的外鍵約束。這失敗了,因為當時資料庫中還沒有 session!

我知道我可以簡單地在 session_start() 之後,必要時手動呼叫寫入處理器,強制在資料庫中建立該列,但我不確定這是否是最佳方法。

一旦我找到「優雅」的解決方案,或完全不同的方法,我會發布一些可執行的範例程式碼。

同時... 祝你玩得開心!
1
chris at ocproducts dot com
7 年前
啟動 session 可能會覆寫您自訂的快取控制標頭,這可能會導致點擊返回按鈕回到之前的 POST 請求時出現問題(至少在 Chrome 上是這樣)。
在我的系統上,它設定了 'no-store',這比 'no-cache' 嚴重得多,也是導致返回按鈕失效的原因。

如果您仔細地控制自己的快取標頭,您需要呼叫
session_cache_limiter('');

...來停止它變更您的快取控制標頭。
1
fabmlk at hotmail dot com
8 年前
如果您需要在同一個腳本中開啟多個不同的 session,並且仍然讓 PHP 為您產生 session ID,以下是我提出的一個簡單函式(假設使用 PHP 預設的 session 處理器):

<?php
/**
* 切換到或透明地建立名稱為 $name 的 session。
* 它可以很容易地擴展以管理不同的 session 生存時間。
*/
function session_switch($name = "PHPSESSID") {
static
$created_sessions = array();

if (
session_id() != '') { // 如果目前已開啟 session,則關閉它
session_write_close();
}
session_name($name);
if (isset(
$_COOKIE[$name])) { // 如果已存在特定的 session,則與 $created_sessions 合併
$created_sessions[$name] = $_COOKIE[$name];
}
if (isset(
$created_sessions[$name])) { // 如果已存在 session,則模擬它
session_id($created_sessions[$name]);
session_start();
} else {
// 建立新的 session
session_start();
$_SESSION = array(); // 在複製 session 檔案之前清空內容
// 使用新的 ID 和目前的 $_SESSION 內容複製上一個 session 檔案
// 如果這是第一個建立的 session,則沒有可複製的內容,並且傳遞 true 作為引數將會處理「僅建立」一個 session 檔案
session_regenerate_id(empty($created_sessions));
$created_sessions[$name] = session_id();
}
}

session_switch("SESSION1");
$_SESSION["key"] = "value1"; // 專屬於 session 1
session_switch("SESSION2");
$_SESSION["key"] = "value2"; // 專屬於 session 2
session_switch("SESSION1");
// 回到 session 1
// ...
?>

當使用此函式時,session_start() 不應再單獨呼叫(可以使用不帶引數的 session_switch() 呼叫來取代)。
另請記住,session_start() 每次呼叫都會設定 Set-Cookie HTTP 標頭,因此如果您在 session 之間輸出內容,請使用輸出緩衝包裝。

注意:處理多個 session 可能很少是個好主意,因此如果您認為自己有很好的使用案例,請再三考慮。
就我個人而言,它在一些我必須維護的舊程式碼的快速修補中發揮了作用。
1
info at nospam dot mmfilm dot sk
14 年前
對於那些在 UTF-8 編碼檔案中遇到問題的人

我因為 BOM 而收到錯誤,即使我將 Dreamweaver 設定為「另存新檔」時不包含 BOM。看來 DW 不會變更已存在檔案中的此設定。在使用不含 BOM 建立新檔案之後,一切都運作良好。

我也建議使用 http://people.w3.org/rishida/utils/bomtester/index.php - 一個遠端檢查 BOM 是否存在的工具。
0
polygon dot co dot in at gmail dot com
26 天前
<?php
session_start
([
'read_and_close' => true,
]);
?>

我認為 read_and_close 選項的作用是作為唯讀模式的 session

令人驚訝的是,即使 HTTP 請求中不存在 sessionId cookie,它也能運作並產生新的 sessionId。

其次,使用此選項的函式也會產生關於新產生 sessionId 的新 session 檔案

第三,如果 sessionId 無效(沒有關於 sessionId 的檔案),它會建立一個新的。
0
theking2(at)king.ma
5 個月前
我通常使用以下方式來啟動新的 session

<?php
session_start
( [
'name' => DEBUG ? 'SessionId' : '__Secure-SessionId',
'cookie_lifetime' => 0,
'cookie_path' => '/',
'cookie_secure' => true,
'cookie_httponly' => true,
'cookie_samesite' => 'Strict',
'sid_length' => 96,
'sid_bits_per_character' => 5,
'use_strict_mode' => true,
'referer_check' => $_SERVER['HTTP_HOST'],
] );
?>

這會建立一個 session,其中 session cookie 名稱中的熵增加、需要安全的 session,並確保惡意參照者無法存取我的登入頁面等等。

根據 MDN[1],安全的 session 必須設定前綴 "__Secure-"。

[1](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie)
0
axew3 at axew3 dot com
7 年前
我需要用簡單的方式,計算頁面在網站上重新載入的次數,當計數器為 0 時,可以新增警告彈出視窗...
session_start();
if(isset($_SESSION['count'])){
$count = $_SESSION['count'];
$count++;
$count = $_SESSION['count'] = $count;
} else {
$count = $_SESSION['count'] = 0;
}
echo $count;

//session_destroy();
0
Charlie at NOSPAM dot example dot com
15 年前
請注意,取決於腳本結尾來關閉 session 將有效地序列化並行 session 請求。期望並行擷取資料的並行背景「資料擷取」(例如 AJAX 或 amfphp/Flex 等應用程式)很容易陷入這個陷阱。

將 session_write_close 保留到昂貴的操作之後也存在問題。

為了盡量減少影響,請盡早呼叫 session_write_close(也稱為 session_commit)(例如,在不引入競爭條件的情況下),或者避免序列化瓶頸。
-1
axew3 at axew3 dot com
7 年前
我只需要用簡單的方式,計算頁面在網站上重新載入的次數,當計數器為 0 時,可以新增警告彈出視窗

session_start();
if(isset($_SESSION['count'])){
$count = $_SESSION['count'];
$count++;
$count = $_SESSION['count'] = $count;
} else {
$count = $_SESSION['count'] = 0;
}
echo $count;

//session_destroy();
-1
polygon dot co dot in at gmail dot com
3 年前
網站很容易遭受 Session 攻擊,原因是沒有正確使用 session。

有許多工具(例如「Apache Benchmark」(ab))和其他許多工具可以用負載來攻擊網站,以進行負載/效能測試。

以下程式碼針對每個請求啟動 session。

<?php
session_start
();

$username = $_POST['username'];
$password = $_POST['password'];

if(
isValidUser($username, $password)) {

Suserdetails = getUserDetails($username);

$_SESSION['user_id'] = Suserdetails['user_id'];
$_SESSION['username'] = Suserdetails['username'];
$_SESSION['firstname'] = Suserdetails['firstname'];

header('Location: dashboard.php');
}
?>

當我使用像 ab 這樣的工具時,這會針對每個請求產生 session 檔案,而不考慮 PHPSESSID cookie 值,從而產生 inode 問題。

應該在正確驗證之後啟動 session。

<?php

$username
= $_POST['username'];
$password = $_POST['password'];

if(
isValidUser($username, $password)) {

Suserdetails = getUserDetails($username);

session_start();

$_SESSION['user_id'] = Suserdetails['user_id'];
$_SESSION['username'] = Suserdetails['username'];
$_SESSION['firstname'] = Suserdetails['firstname'];

header('Location: dashboard.php');
}
?>

除了登入之外的其他腳本,首先會驗證需要 session 的 session。

<?php

if(session_status()!=PHP_SESSION_NONE) header('Location: login.php');

session_start();

if(!isset(
$_SESSION['user_id'])) header('Location: login.php');

code logic below....
}
?>

此範例適用於基於檔案的 session。
對於其他 session 模式,請檢查函式 session_set_save_handler。
-2
anon at ymous dot com
13 年前
我嘗試讓瀏覽器呼叫建立的 session,可以被命令列 cli->curl php 呼叫使用(在此情況下,兩個呼叫都針對相同的伺服器和 php.ini),用於一組彈性的媒體匯入常式,

但是 cli->curl 呼叫總是會啟動新的 session,儘管我將 PHPSESSID=validID 作為 curl 呼叫的 URL 的第一個參數。

我能夠透過在透過 curl 呼叫的腳本中呼叫 session_start() 之前呼叫 session_id($_GET['PHPSESSID']) 來修復此問題。
-3
sanjuro at 1up-games dot com
13 年前
SID 的問題在於,如果您在某些情況下沒有啟動 session,它會回傳一般未定義的常數通知,而不是輸出空字串以進行透明整合。因此,您可能需要事先使用 defined() 測試常數。
-4
info.at.merchandisinginteractive.sk
14 年前
一個方便的腳本,可檢查目前目錄開始的所有目錄中的所有檔案中是否存在 uft-8 位元組順序標記 (BOM)。結合了這裡其他人的工作...

<?php
function fopen_utf8 ($filename) {
$file = @fopen($filename, "r");
$bom = fread($file, 3);
if (
$bom != b"\xEF\xBB\xBF")
{
return
false;
}
else
{
return
true;
}
}

function
file_array($path, $exclude = ".|..|design", $recursive = true) {
$path = rtrim($path, "/") . "/";
$folder_handle = opendir($path);
$exclude_array = explode("|", $exclude);
$result = array();
while(
false !== ($filename = readdir($folder_handle))) {
if(!
in_array(strtolower($filename), $exclude_array)) {
if(
is_dir($path . $filename . "/")) {
// Need to include full "path" or it's an infinite loop
if($recursive) $result[] = file_array($path . $filename . "/", $exclude, true);
} else {
if (
fopen_utf8($path . $filename) )
{
//$result[] = $filename;
echo ($path . $filename . "<br>");
}
}
}
}
return
$result;
}

$files = file_array(".");
?>
-3
dstuff at brainsware dot org
15 年前
名稱中的空格似乎也不起作用 - 每次都會產生新的 session id
-4
m dot kuiphuis at hccnet dot nl
21 年前
[編輯註:有關此的更多資訊
http://www.zvon.org/tmRFC/RFC882/Output/chapter5.html ]

我在 Linux 上使用 Apache 和 PHP 4.3.2 進行基於名稱的虛擬主機。
每次當我重新整理(在 Internet Explorer 中按 F5)時,我注意到我獲得了一個新的 session_id。同時使用 Netscape 瀏覽同一網站並沒有給我帶來這個問題。首先我認為這是一些 PHP 問題(在我用 Netscape 測試之前),但在網路上搜尋了很多後,我找到了問題所在。
由於我為我的測試伺服器使用了基於名稱的虛擬主機,並且我們為不同的客戶提供不同的網路商店,因此我使用語法 webshop_customername.servername.nl 作為網域名稱。
網域名稱中的 _ 似乎是問題所在。當網域名稱中有特殊字元(例如 _)時,Internet Explorer 只是拒絕在用戶端設定 cookie。有關此問題的更多資訊:http://support.microsoft.com/default.aspx?scid=kb;EN-US;316112
很蠢的是,此資訊與 asp 相關 (yuk :o)
-5
leandroico---at---gmail---dot---com
17 年前
標籤:session_start 標頭 輸出 錯誤 include_once require_once php 標籤 新行

與在 include 檔案中呼叫 *session_start()* 相關的輸出標頭錯誤。

如果您在 include 檔案中啟動您的 session,您必須注意 php 結束標籤後是否存在不需要的字元。

讓我們來看一個例子
> page.php
<?php
include_once 'i_have_php_end_tag.inc.php';
include_once
'init_session.inc.php';

echo
"該死!我為什麼會有這些輸出標頭錯誤?";
?>

> i_have_php_end_tag.inc.php
<?php
$_JUST_A_GLOBAL_VAR
= '是的,確實是一個全域變數';
?>

> init_session.inc.php
<?php
session_start
();
$_SESSION['blabla'] = 123;
?>

有了這些東西,我們會得到一個錯誤,類似於
"... 無法發送 session 快取限制器 - 標頭已發送(輸出開始於 ...",對吧?

為了解決這個問題,我們必須忽略 include 檔案發送的所有輸出。為了確保我們需要使用一對函式:*ob_start()* 和 *ob_end_clean()* 來抑制輸出。所以,我們所要做的就是將 *page.php* 更改為這樣

<?php
ob_start
();
include_once
'i_have_php_end_tag.inc.php';
include_once
'init_session.inc.php';
ob_end_clean();

echo
"哇呼!好極了!消滅你這些不需要的輸出!!!";
?>
-4
james at skinsupport dot com
18 年前
有一點值得注意,它讓我困擾了三天

重要的是要注意,Firefox(舉例來說)會自動對伺服器進行兩次呼叫。一個用於頁面,一個用於 favicon.ico。

如果您在頁面存在時將 session 變數(就像我一樣)設定為特定值,而在頁面不存在時設定為其他值,則如果 favicon.ico 不存在,則不存在頁面的值會覆蓋現有頁面的值。

我懷疑你們很多人都在這樣做,但是如果您這樣做,這是一個您需要解決的問題,否則您在三天的時間內就會變成禿頭!
To Top