PHP Conference Japan 2024

session_decode

(PHP 4, PHP 5, PHP 7, PHP 8)

session_decode從 session 編碼字串解碼 session 資料

描述

session_decode(字串 $data): 布林值

session_decode() 解碼在 $data 中提供的序列化 session 資料,並將結果填入 $_SESSION 超級全域變數中。

預設情況下,使用的反序列化方法是 PHP 內部方法,與 unserialize() 不同。可以使用 session.serialize_handler 設定序列化方法。

參數

data

要儲存的編碼資料。

回傳值

成功時回傳 true,失敗時回傳 false

參見

新增註解

使用者貢獻的註解 16 個註解

48
Frits dot vanCampen at moxio dot com
12 年前
我注意到發佈的手動解碼 session 的解決方案並不完美,所以我貢獻了一個更穩健的解決方案。

preg_match 解決方案永遠行不通。找到可能破壞反序列化的案例並不難。
在 jason-joeymail 的情況下,它會在

<?php
$_SESSION
["test"] = ";oops|";
?>

以下是我的解決方案。它不使用正規表示式,而是使用序列化運算的可逆性以及序列化認為完成時會忽略所有後續輸入的「功能」。它絕不是一個美觀或特別快速的解決方案,但它是一個更穩健的解決方案。
我已為 "php" 和 "php_binary" 新增了一個反序列化程式。為 "wddx" 新增一個反序列化程式應該是微不足道的。

<?php
class Session {
public static function
unserialize($session_data) {
$method = ini_get("session.serialize_handler");
switch (
$method) {
case
"php":
return
self::unserialize_php($session_data);
break;
case
"php_binary":
return
self::unserialize_phpbinary($session_data);
break;
default:
throw new
Exception("不支援的 session.serialize_handler: " . $method . ". 支援:php, php_binary");
}
}

private static function
unserialize_php($session_data) {
$return_data = array();
$offset = 0;
while (
$offset < strlen($session_data)) {
if (!
strstr(substr($session_data, $offset), "|")) {
throw new
Exception("無效資料,剩餘: " . substr($session_data, $offset));
}
$pos = strpos($session_data, "|", $offset);
$num = $pos - $offset;
$varname = substr($session_data, $offset, $num);
$offset += $num + 1;
$data = unserialize(substr($session_data, $offset));
$return_data[$varname] = $data;
$offset += strlen(serialize($data));
}
return
$return_data;
}

private static function
unserialize_phpbinary($session_data) {
$return_data = array();
$offset = 0;
while (
$offset < strlen($session_data)) {
$num = ord($session_data[$offset]);
$offset += 1;
$varname = substr($session_data, $offset, $num);
$offset += $num;
$data = unserialize(substr($session_data, $offset));
$return_data[$varname] = $data;
$offset += strlen(serialize($data));
}
return
$return_data;
}
}
?>

用法

<?php
Session
::unserialize(session_encode());
?>
4
petej*shaman_ca
21 年前
這個函數的行為似乎在 4.1.2 和 4.3.3 之間發生了變化。在 4.1.2 中,session_decode() 不在意 session 是否已啟動,只會將字串解碼到 _SESSION 陣列中。在我的 4.3.3 安裝中,除非我明確使用 session_start() 啟動 session,否則 session_decode() 無法運作。
7
frank at interactinet dot com
11 年前
我發現這是最簡單的解決方案

<?php
// 如果 session 還沒啟動
session_start();

// 儲存目前的 session
$my_sess = $_SESSION;

// 解碼 $data (已編碼的 session 資料,可能來自檔案或資料庫)。請注意,解碼後的資料會直接放入 $_SESSION
session_decode($data);
$data = $_SESSION;

print_r($data);

// 還原我們自己的 session
$_SESSION = $my_sess;

?>
4
leon dot pegg at gmail dot com
18 年前
我發現這是一個更好的方法,可以在保留目前 session 的同時還原 session 資料。

function decode_session($session_string){
$current_session = session_encode();
foreach ($_SESSION as $key => $value){
unset($_SESSION[$key]);
}
session_decode($session_string);
$restored_session = $_SESSION;
foreach ($_SESSION as $key => $value){
unset($_SESSION[$key]);
}
session_decode($current_session);
return $restored_session;
}

enjoy
2
forum at orthanc dot co dot nz
20 年前
如果你想要切換出既有的 session,而不是將 session 載入到一個乾淨的狀態,使用這個方法要小心。

session_decode 並不會銷毀既有的 session 資料,如果存在相同名稱的 session 變數,它會覆寫,但如果名稱不衝突,既有的 session 變數就會留下來。

我還沒有找到比下面更好的解決方案:

session_destroy()
session_start()
session_decode(....);

-----------------------------------------
為了說明我的意思:

<?
session_start();
$a = 5;
session_register('a');
session_decode("<沒有將 a 作為 session 變數的 session>");
print (session_is_registered('a') ? $a : 'Not Registered' );
?>

上述程式碼會印出 '5',因為 $a 並沒有被 session_decode 銷毀或取消註冊。
1
deminy at deminy dot net
17 年前
1. 回覆 ac 的貼文

正如我在 http://us.php.net/manual/en/function.unserialize.php#76977 中提到的,如果 session 資料包含含有字元 '|' 的字串變數,則用於反序列化 PHP session 資料的正規表示式將無法運作。

以下是一個簡單的範例,展示當函式 unserializesession() 無法運作的情況。

<?php
function unserializesession($data) {
$vars=preg_split('/([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff^|]*)\|/',
$data,-1,PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
for(
$i=0; $vars[$i]; $i++) $result[$vars[$i++]]=unserialize($vars[$i]);
return
$result;
}

session_start();

$_SESSION['var'] = 'a|b';
$str = session_encode();
$arr = unserializesession($str);

print_r($_SESSION);
echo
"<br />\n";
print_r($arr);
?>

2. 回覆 bmorel 的貼文

您的函式 session_real_decode() 在不涉及 session 函式的情況下解碼 session 資料非常好。它在大多數情況下都有效,但在處理參考變數時,還有另一種情況也應該處理。

<?php case 'r': /* 參考 */ // 小寫的 R ?>

我幾個月前在 PHP 5.1.6 上發現這個錯誤,但不知道它是否也存在於其他版本的 PHP 中。此外,我不確定在處理其他資料類型時是否存在類似的錯誤。

因此,以下是我修改此函式的建議:
2.1.
將 switch 敘述從
<?php switch ($str[$q]) { ?>
改為
<?php switch (strtolower($str[$q])) { ?>
2.2.
在所有 case 敘述中,僅使用小寫字元進行字元比較。例如:
<?php case 'R': /* 參考 */ ?>
應該寫成
<?php case 'r': /* 參考 */ ?>

(我不想在這裡放置長程式碼,所以只留下程式碼片段以節省空間)
1
davyvandenbremt at gmail dot com
13 年前
以下是我們用於反序列化 session 的方法。

<?php
function unserialize_session($val) {
$result = array();

// 在開頭加上分號,以便更容易編寫正規表示式
$val = ';' . $val;

// 正規表示式,用於尋找鍵
$keyreg = '/;([^|{}"]+)\|/';

// 尋找所有鍵
$matches = array();
preg_match_all($keyreg, $val, $matches);

// 只有在找到一些鍵時才繼續
if (isset($matches[1])) {
$keys = $matches[1];

// 透過以鍵的正規表示式分割輸入來尋找值
$values = preg_split($keyreg, $val);

// 將第一個值移出,因為它始終為空(由於我們的前綴分號)
if (count($values) > 1) {
array_shift($values);
}

// 合併 $keys 和 $values
$result = array_combine($keys, $values);
}

return
$result;
}
?>
0
fabrizio dot messina at gmail dot com
19 年前
這個函式 _真的_ 分割和解碼 session 資料

function unserializesession($data) {
$vars=preg_split('/([a-zA-Z0-9]+)\|/',$data,-1,PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
for($i=0; $vars[$i]; $i++) {
$result[$vars[$i++]]=unserialize($vars[$i]);
}
return $result;
}

與先前發布的 'unserializesession' 函式的不同之處在於 preg_split 函式中的正規表示式('[a-zA-Z0-9]+' vs '[a-z,A-Z]+' )
0
sco at postmaster dot co dot uk
20 年前
如果您嘗試從常規 PHP session 函式之外存取您的 session 資料,您可能會想要使用 WDDX 作為您的序列化程式,而不是正常的 PHP 序列化程式。當您的資料序列化為 XML 時,顯然可以隨意反序列化。

WDDX 看起來速度稍微慢一點,並且它建立的文字字串比正常的 PHP 序列化程式建立的要大得多,但是它以最小的麻煩提供了此功能。

Donal
-1
vesely at tana dot it
19 年前
當使用此函式來管理 session 時,最好
關閉 register_globals。然後,可以
檢查給定其 ID 的 session 內容。

<?php
$fname
= session_save_path() . "/sess_" . $the_sid;
if (
session_decode(file_get_contents($fname)))
{
$vars = $_SESSION;
$_SESSION = array();

// 檢查 $vars...
}
?>

根據 PHP 版本,您可能需要啟動一個虛擬的
session,上面的程式碼才能運作。我立即重設
$_SESSION 以避免寫入
虛擬 session:這是測試程式碼時需要的!
-2
jason at joeymail dot net
13 年前
又一次嘗試重新發明輪子,使用帶偏移的 match,而不是 split...

<?php
function unserializesession( $data )
{
if(
strlen( $data) == 0)
{
return array();
}

// 匹配所有 session 鍵和偏移量
preg_match_all('/(^|;|\})([a-zA-Z0-9_]+)\|/i', $data, $matchesarray, PREG_OFFSET_CAPTURE);

$returnArray = array();

$lastOffset = null;
$currentKey = '';
foreach (
$matchesarray[2] as $value )
{
$offset = $value[1];
if(!
is_null( $lastOffset))
{
$valueText = substr($data, $lastOffset, $offset - $lastOffset );
$returnArray[$currentKey] = unserialize($valueText);
}
$currentKey = $value[0];

$lastOffset = $offset + strlen( $currentKey )+1;
}

$valueText = substr($data, $lastOffset );
$returnArray[$currentKey] = unserialize($valueText);

return
$returnArray;
}
?>
-1
erwinmoller at xs4all dot nl
19 年前
先前提到的 regExp 方法並非在所有情況下都有效。

如果我輸入這個
voornaam|s:8:"Ai|;\'\"";achternaam|s:6:"werrwe";leeftijd|i:44;

我會得到這個
array(4) {
["voornaam"]=>
bool(false)
["Ai"]=>
bool(false)
["achternaam"]=>
string(6) "werrwe"
["leeftijd"]=>
int(44)
}

而我預期的是
array(3) {
["voornaam"]=>
string(8) "Ai|;\'\""
["achternaam"]=>
string(6) "werrwe"
["leeftijd"]=>
int(44)
}

我認為 | 符號搞砸了事情。:-/
-1
brett at brettbrewer dot com
19 年前
我對 fabrizio (等人) 的 unserializesession 函式做了一個小修改,因為它在我的變數名稱中使用底線時會出錯。以下是正確的版本,它應該可以處理所有可能的 PHP 變數名稱

function unserializesession($data) {
$vars=preg_split(
'/([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\|/',
$data,-1,PREG_SPLIT_NO_EMPTY |
PREG_SPLIT_DELIM_CAPTURE
);
for($i=0; $vars[$i]; $i++) {
$result[$vars[$i++]]=unserialize($vars[$i]);
}
return $result;
}

請注意,由於這個論壇的限制,我必須將上面的 preg_split 函式呼叫拆分成 4 行。這個版本更改了用於尋找變數名稱的 regex,使其符合 PHP 手冊中指定的變數名稱規範,網址為 http://us3.php.net/manual/en/language.variables.php. 我只是直接從 PHP 手冊頁面中取出 regex,它們給出了有效變數名稱的 regex 等效值為

[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*

總之,現在對我來說效果很好,即使在巨大的編碼 session 資料字串上也是如此。
-2
ac
17 年前
這個解決了我的 | 問題

function unserializesession($data) {
$vars=preg_split('/([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff^|]*)\|/',
$data,-1,PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
for($i=0; $vars[$i]; $i++) $result[$vars[$i++]]=unserialize($vars[$i]);
return $result;
}
-3
njail
20 年前
<?PHP
// 取得 Session 內容
$varsess = Array('SESSION');
for (
$i = 0; $i < sizeof($varsess); $i++)
{
if (
is_array(${"_{$varsess[$i]}"}))
{
foreach (${
"_{$varsess[$i]}"} as $var=>$val)
{
$
$var = $val;
// print "Var :".$var." -- Value :".$val."\n<br>";
}
}
unset(${
"_{$varsess[$i]}"});
}
?>
-5
xueron at gmail dot com
18 年前
一個 perl reg

$s = session_encoded_value;
%res = $s =~ /([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\|([^\|]*[\;\}])/g;
To Top