Session 函数
在线手册:中文 英文
PHP手册

session_decode

(PHP 4, PHP 5)

session_decodeDecodes session data from a session encoded string

说明

bool session_decode ( string $data )

session_decode() decodes the serialized session data provided in $data, and populates the $_SESSION superglobal with the result.

Please note the unserialization method is not the same as unserialize(). The serialization method is internal to PHP and can be set using session.serialize_handler.

参数

data

The encoded data to be stored.

返回值

成功时返回 TRUE, 或者在失败时返回 FALSE.

参见


Session 函数
在线手册:中文 英文
PHP手册
PHP手册 - N: Decodes session data from a session encoded string

用户评论:

Frits dot vanCampen at moxio dot com (24-Mar-2012 01:24)

I noticed that the posted solutions for manually decoding sessions are not perfect, so I've contributed a more robust solution.

The preg_match solution can never work. It's not so hard to find a case that might break unserialization.
In the case of jason-joeymail is breaks on:

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

Below you can find my solution. It doesn't use a regular expression but rather the reversibility of the serialize operation and the 'feature' that serialize ignores all further input when it thinks it's done. It's by no means a beautiful or particularly fast solution but it is a more robust solution.
I've added a deserializer for "php" and "php_binary". It should be trivial to add one for "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("Unsupported session.serialize_handler: " . $method . ". Supported: 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("invalid data, remaining: " . 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;
    }
}
?>

Usage:

<?php
Session
::unserialize(session_encode());
?>

jason at joeymail dot net (05-Jan-2011 12:23)

Yet another attempt a wheel re-invention, using match with offsets rather than split...

<?php
function unserializesession( $data )
{
    if( 
strlen( $data) == 0)
    {
        return array();
    }
   
   
// match all the session keys and offsets
   
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;
}
?>

davyvandenbremt at gmail dot com (16-Dec-2010 09:18)

Here's what we are using to unserialize the session.

<?php
function unserialize_session($val) {
 
$result = array();
 
 
// prefixing with semicolon to make it easier to write the regular expression
 
$val = ';' . $val;
 
 
// regularexpression to find the keys
 
$keyreg = '/;([^|{}"]+)\|/';
 
 
// find all keys
 
$matches = array();
 
preg_match_all($keyreg, $val, $matches);
 
 
// only go further if we found some keys
 
if (isset($matches[1])) {
   
$keys = $matches[1];
   
   
// find the values by splitting the input on the key regular expression
   
$values = preg_split($keyreg, $val);
   
   
// unshift the first value since it's always empty (due to our semicolon prefix)
   
if (count($values) > 1) {
     
array_shift($values);
    }
   
   
// combine the $keys and $values
   
$result = array_combine($keys, $values);
  }
 
  return
$result;
}
?>

deminy at deminy dot net (16-Nov-2007 09:24)

1. Reply to ac's post:

As I mentioned at http://us.php.net/manual/en/function.unserialize.php#76977 , the regular expression used to unserialize a PHP session data won't work if the session data contains string variable which contains character '|'.

Here is a simple example I just come up with to show when the function unserializesession() won't work.

<?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. Reply to bmorel's post:

Your function session_real_decode() is very nice to decode session data without involving with session functions. It works in most cases, but when dealing with reference variables, there is another case which should also be handled:

    <?php case 'r': /* reference  */ // R in lowercase ?>

I found this bug on PHP 5.1.6 several months ago, but don't know if it exsits in other version of PHP or not. Also, I am not sure if similiar bugs exist when handling other data types.

So here is my suggestion to revise the function,
2.1.
    change the switch statement from
    <?php switch ($str[$q]) { ?>
    to
    <?php switch (strtolower($str[$q])) { ?>
2.2.
    In all case statements, use lowercase characters only for character comparison. For example,
    <?php case 'R': /* reference  */ ?>
    should be be written as
    <?php case 'r': /* reference  */ ?>

(I don't want to put long code here, so just leave pieces of code to save spaces)

ac (21-Oct-2007 03:46)

this solved my |?problem:

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;
}

leon dot pegg at gmail dot com (23-Aug-2006 04:14)

i have found this to be a better way to restore session data while keeping your current 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

xueron at gmail dot com (15-Jul-2006 05:30)

a perl reg:

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

nero321 at zmail dot sk (27-Jan-2006 01:01)

Another solution for decoding session string to "$_SESSION"-like array:

<?php

// returns decoded string as arrays of variables
//   or false on error (when session_decode returns false)
function DecodeSession($sess_string)
{
   
// save current session data
    //   and flush $_SESSION array
   
$old = $_SESSION;
   
$_SESSION = array();

   
// try to decode passed string
   
$ret = session_decode($sess_string);
    if (!
$ret) {
       
// if passed string is not session data,
        //   retrieve saved (old) session data
        //   and return false
       
$_SESSION = array();
       
$_SESSION = $old;

        return
false;
    }

   
// save decoded session data to sess_array
    //   and flush $_SESSION array
   
$sess_array = $_SESSION;
   
$_SESSION = array();

   
// restore old session data
   
$_SESSION = $old;

   
// return decoded session data
   
return $sess_array;
}

?>

Example of use:

<?php

$sarr
= DecodeSession($sess_str);
print_r($sarr);

?>

But I have not tested performance.

erwinmoller at xs4all dot nl (18-Oct-2005 03:25)

The regExp-method describe earlier doesn't work under all conditions.

If I feed this:
voornaam|s:8:"Ai|;\'\"";achternaam|s:6:"werrwe";leeftijd|i:44;

I get this:
array(4) {
  ["voornaam"]=>
  bool(false)
  ["Ai"]=>
  bool(false)
  ["achternaam"]=>
  string(6) "werrwe"
  ["leeftijd"]=>
  int(44)
}

while I expected:
array(3) {
  ["voornaam"]=>
  string(8) "Ai|;\'\""
  ["achternaam"]=>
  string(6) "werrwe"
  ["leeftijd"]=>
  int(44)
}

I think the | is messing things up. :-/

bmorel at ssi dot fr (23-Aug-2005 08:54)

Here is a function that returns decoded session data, that seems to work in every cases, even when strings contain reserved chars :

<?php
define
('PS_DELIMITER', '|');
define('PS_UNDEF_MARKER', '!');

function
session_real_decode($str)
{
   
$str = (string)$str;

   
$endptr = strlen($str);
   
$p = 0;

   
$serialized = '';
   
$items = 0;
   
$level = 0;

    while (
$p < $endptr) {
       
$q = $p;
        while (
$str[$q] != PS_DELIMITER)
            if (++
$q >= $endptr) break 2;

        if (
$str[$p] == PS_UNDEF_MARKER) {
           
$p++;
           
$has_value = false;
        } else {
           
$has_value = true;
        }
       
       
$name = substr($str, $p, $q - $p);
       
$q++;

       
$serialized .= 's:' . strlen($name) . ':"' . $name . '";';
       
        if (
$has_value) {
            for (;;) {
               
$p = $q;
                switch (
$str[$q]) {
                    case
'N': /* null */
                   
case 'b': /* boolean */
                   
case 'i': /* integer */
                   
case 'd': /* decimal */
                       
do $q++;
                        while ( (
$q < $endptr) && ($str[$q] != ';') );
                       
$q++;
                       
$serialized .= substr($str, $p, $q - $p);
                        if (
$level == 0) break 2;
                        break;
                    case
'R': /* reference  */
                       
$q+= 2;
                        for (
$id = ''; ($q < $endptr) && ($str[$q] != ';'); $q++) $id .= $str[$q];
                       
$q++;
                       
$serialized .= 'R:' . ($id + 1) . ';'; /* increment pointer because of outer array */
                       
if ($level == 0) break 2;
                        break;
                    case
's': /* string */
                       
$q+=2;
                        for (
$length=''; ($q < $endptr) && ($str[$q] != ':'); $q++) $length .= $str[$q];
                       
$q+=2;
                       
$q+= (int)$length + 2;
                       
$serialized .= substr($str, $p, $q - $p);
                        if (
$level == 0) break 2;
                        break;
                    case
'a': /* array */
                   
case 'O': /* object */
                       
do $q++;
                        while ( (
$q < $endptr) && ($str[$q] != '{') );
                       
$q++;
                       
$level++;
                       
$serialized .= substr($str, $p, $q - $p);
                        break;
                    case
'}': /* end of array|object */
                       
$q++;
                       
$serialized .= substr($str, $p, $q - $p);
                        if (--
$level == 0) break 2;
                        break;
                    default:
                        return
false;
                }
            }
        } else {
           
$serialized .= 'N;';
           
$q+= 2;
        }
       
$items++;
       
$p = $q;
    }
    return @
unserialize( 'a:' . $items . ':{' . $serialized . '}' );
}
?>

Please let met know if you find any bug.

brett at brettbrewer dot com (29-Jul-2005 07:25)

I have made a minor change to fabrizio's (et all) version of the unserializesession function because it was choking on underscores in my variable names.  Here is the correct version which should account for ALL possible PHP variable names:

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;
}

Please note that I had to split the preg_split function call above into 4 lines due to the limitations of this forum. This version changes the regex used to find variable names so that it complies with the specs for  variable names as specified in the PHP manual at http://us3.php.net/manual/en/language.variables.php. I just took the regex directly from the PHP manual pages where they give the regex equivalent for a valid variable name as:

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

Anyway, this seems to work great for me now, even on gigantic strings of encoded session data.

vesely at tana dot it (26-May-2005 11:25)

When using this function to manage sessions, it is MUCH
better to have register_globals turned off. Then one can
examine the session content given its id.

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

     
// examine $vars...
  
}
?>

Depending on PHP version, you may need to have a dummy
session started for the code above to work. I reset the
$_SESSION immediately in order to avoid writing the
dummy session: that's needed while testing the code!

fabrizio dot messina at gmail dot com (16-May-2005 02:40)

this function _really_ split and decode session data:

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;
}

the difference from previously posted 'unserializesession' function is the regular expression inside function preg_split ('[a-zA-Z0-9]+' vs  '[a-z,A-Z]+' )

sco at postmaster dot co dot uk (04-Jun-2004 03:52)

If you're trying to access your session data from outside the regular php session functions, you might want to use WDDX as your serializer, as opposed to the normal php serializer. When your data is serialized as XML, obviously it's easy to unserialize as you please.

WDDX seems to be a little slower, and the text string it creates is much bigger than that created by the normal php serializer, but it provides the functionality with minimal hassle.

Donal

forum at orthanc dot co dot nz (02-Apr-2004 08:26)

Becarful using this if you are trying to switch out of an existing session rather than load one into a clean slate.

session_decode doesn't destroy the existing session data, it will over write it if there is a session variable of the same name, but if the names don't clash the existing session variables will hang around.

I have yet to find a better solution than

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

-----------------------------------------
To explain what I'm talking about

<?
    session_start();
    $a = 5;
    session_register('a');
    session_decode("<session that doesn't have a as a session variable>");
    print (session_is_registered('a') ? $a : 'Not Registered' );
?>

The above code will print '5' as $a hasn't been destroyed or even unregistered by the session_decode

njail (12-Dec-2003 08:28)

<?PHP
// Get Session Content
$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]}"});
}
?>

petej*shaman_ca (21-Aug-2003 09:44)

Seems like there was a change in the behavior of this function somewhere between 4.1.2 and 4.3.3.  In 4.1.2 session_decode() didn't care whether the session was started, and would just decode the string into the _SESSION array.  In my 4.3.3 install, session_decode() wouldn't work unless I explicitly started the session with session_start().