会话函数

目录

添加备注

用户贡献的备注 23 备注

Edemilson Lima <pulstar at gmail dot com>
17 年前
会话和浏览器标签

您可能已经注意到,当您在 Firefox、Opera、IE 7.0 中的两个或多个标签中打开您的网站,或者在 IE 6.0 中使用“Control+N”打开一个新窗口时,它会使用相同的 cookie 或传递相同的会话 ID,因此另一个标签只是先前标签的副本。您在一个标签中所做的操作会影响另一个标签,反之亦然。即使您再次打开 Firefox,它也会使用先前会话的相同 cookie。但这并不是您大多数时候需要的,尤其是在您想在 Web 应用程序中将信息从一个地方复制到另一个地方时。这是因为默认会话名称是“PHPSESSID”,所有标签都会使用它。有一个解决方法,它仅依赖于更改会话名称。

将这些行放在您的主脚本(调用子脚本的脚本)的顶部,或者放在您拥有的每个脚本的顶部

<?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('') 将为新的会话名称生成一个唯一 ID。它不需要像 uniqid(rand(),TRUE) 那样非常强大,因为所有安全性都依赖于会话 ID,而不是会话名称。我们这里只需要为我们打开的每个会话提供不同的 ID。即使使用 getmypid() 也足够用于此,但我不知道这是否会对 Web 服务器构成威胁。我认为不会。

output_add_rewrite_var() 将自动向网站中的每个链接和 Web 表单添加一对“SESSION_NAME=SESSxxxxx”。但是,为了正常工作,您需要将其手动添加到您拥有的任何 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() 将定义脚本将使用的实际会话的名称。

因此,每个链接、表单、header() 和 JavaScript 代码都将 SESSION_NAME 值转发到下一个脚本,它将知道它必须使用哪个会话。如果没有提供,它将生成一个新的会话(因此,为新的标签创建一个新的会话)。

您可能想知道为什么不使用 cookie 来传递 SESSION_NAME 以及会话 ID。好吧,使用 cookie 的问题是,所有标签都将共享同一个 cookie 来执行此操作,并且会话无论如何都会混合在一起。如果将 cookie 设置在不同的路径中,每个 cookie 都将在其自己的目录中可用,那么 cookie 将部分起作用。但这不会使每个标签中的会话彼此完全分离。我认为通过 URL 使用 GET 和 POST 传递会话名称是最好的方法。
pautzomat at web dot de
20 年前
请注意,绝对 URL 不会自动重写为包含 SID。

当然,文档中也这样说(“传递会话 ID”),而且当然对有这样的限制很有道理,但以下是我所遇到的问题
我使用会话已经有一段时间了,没有问题。当我使用一个全局配置文件包含在我所有的脚本中时,它包含这样一行

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

它用于确保所有自动生成的链接都具有正确的前缀(就像 $cfg['PmaAbsoluteUri'] 在 phpMyAdmin 中的工作方式一样)。在引入该变量后,不再有链接传递 SID,导致每个脚本都返回登录页面。我花了几个小时(!!)才认识到这不是我代码中的错误或 php.ini 中的某些错误配置,然后又花了一些时间才弄清楚它是什么。上面的限制完全从我的脑海中消失了(如果曾经存在过...)。

跳过“http:”可以解决问题。

好吧,当然是我自己的错误,但这只是告诉你,人们很容易在几个小时内破坏自己的工作... 只要不要这样做;)
hinom - iMasters
16 年前
简单的会话测试

<?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[$sessVar] = 'hello world';
}else{
if(
$_GET['p'] == 1 ){

// 打印会话值和全局 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() 函数销毁会话
unset( $_SESSION[$sessVar] );

// 检查是否已销毁
if( !isset( $_SESSION[$sessVar] ) ){
echo
'<br>';
echo
$sessName . ' 已被 "取消设置"';
}else{
echo
'<br>';
echo
$sessName . ' 未被 "取消设置"';
}

}
}
?>
<hr>
<a href=tmp.php?p=1>测试 1 (打印会话值)</a>
<br>
<a href=tmp.php?p=2>测试 2 (销毁会话)</a>
Csar
16 年前
Internet Explorer 中存在一个错误,如果服务器名称不是有效的名称,则会话无法正常工作。 例如...如果您的服务器名为 web_server (_ 不是有效字符),如果您调用一个使用会话的页面,例如 http://web_server/example.php,您的会话将无法正常工作,但如果您像这样调用脚本,则会话将正常工作。
[IP 号码]/example.php
Sam Yong - hellclanner at live dot com
13 年前
以下内容已在 PHP 5.3.5 中测试通过。

在脚本执行后设置会话变量,例如在 __destruct 函数中,将不起作用。

<?php

class Example{

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

}

?>

如我通过自定义会话保存处理程序观察到的那样,以上示例不会将任何内容写入临时会话文件。
Jeremy Speer
14 年前
在进行一个项目时,我发现需要在两块不同的软件之间切换实时会话。 执行此操作的文档散布在各个网站上,尤其是在评论部分而不是示例中。 我遇到的一个困难是,其中一个应用程序的会话保存处理程序已设置,而另一个则没有。 现在,我在代码中没有使用 function session_set_save_handler(),而是在我完成该函数后(手动)使用它,但是此函数可以轻松扩展以包含该功能。 基本上,它只覆盖了系统的默认会话保存处理程序。 为了克服这一点,在您使用 getSessionData() 后,只需调用 session_write_close()、session_set_save_handler() 并使用相应的 value,然后使用相应的 value 重新运行 session_name()、session_id() 和 session_start()。 如果您不知道会话 ID,则它位于 $_COOKIE[session_name] 中的字符串,或者如果您使用的是 trans_sid,则位于 $_REQUEST[session_name] 中。[注意:谨慎使用来自 $_REQUEST 的数据,如果可能,请改用 $_GET 或 $_POST,具体取决于页面]。

<?php
function getSessionData ($session_name = 'PHPSESSID', $session_save_handler = 'files') {
$session_data = array();
# 我们是否被告知旧的会话 ID 是什么? 没有这些信息,我们无法继续。
if (array_key_exists($session_name, $_COOKIE)) {
# 保存当前会话 ID
$session_id = $_COOKIE[$session_name];
$old_session_id = session_id();

# 写入并关闭当前会话
session_write_close();

# 获取旧的保存处理程序,并切换到文件
$old_session_save_handler = ini_get('session.save_handler');
ini_set('session.save_handler', $session_save_handler);

# 现在我们可以切换会话,捕获旧的会话名称
$old_session_name = session_name($session_name);
session_id($session_id);
session_start();

# 获取所需的会话数据
$session_data = $_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;
}
?>
hinom06 [at] hotmail.co.jp
13 年前
简单会话测试版本 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() 是否存在。
/*
例如,如果存在但会话无法读取,则必须在 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[$sessVar] = 'hello world';
}else{
if(
$_GET['p'] == 1 ){

// 打印会话值和全局 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() 函数销毁会话
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&<?php echo $sessName . '=' . session_id();?>>测试 1 (打印会话值)</a>
<br>
<a href=tmp.php?p=2&<?php echo $sessName . '=' . session_id();?>>测试 2 (销毁会话)</a>
brfelipe08 at hotmail dot com
14 年前
如果您需要使用会话,并且某些拉丁语系语言需要某些重音符号,则应将文件编码为 ISO-8859-1。
如果您尝试使用 UTF-8(带或不带 BOM),或者 ANSI 将不支持重音符号,则会遇到一些问题。
ISO-8859-1 将同时支持会话和重音符号。
carl /a/ suchideas /o/ com
16 年前
另一个需要注意的是,使用相对 session.save_path 是一个非常糟糕的想法。

如果您非常小心,您可以做到这一点,但请注意以下两个相关要点:

1) 该路径相对于最初执行脚本的目录,因此除非所有页面都从同一目录运行,否则您将必须在每个子文件夹中单独设置该目录。

2) 如果您调用某些函数,例如 session_regenerate_id(),PHP 将尝试相对于可执行文件获取会话目录,或者类似的东西,从而在可执行文件中创建一个错误。这会提供稍微模糊的错误消息,例如:

警告:未知:open(relative_path\ilti9oq3j9ks0jvih1fmiq4sv1.session, O_RDWR) 失败:没有此类文件或目录 (2) 在 Unknown 上的第 0 行

警告:未知:无法写入会话数据(文件)。请验证 session.save_path 的当前设置是否正确 (relative_path) 在 Unknown 上的第 0 行

…所以不要费心了。只需使用

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

(或等效项)在您知道始终与文件处于相同位置的文件中。

{PHP 版本 5.1.6}
LaurentT
15 年前
对于 UNIX

在同一服务器上拥有不同网站时,可能会遇到会话问题:如果一次使用多个网站,会话将合并或崩溃,如果网站由不同的系统用户拥有。例如

www.example.com 存储在 /home/site1/www 中
www.example.net 存储在 /home/site2/www 中

同时使用 www.example.com 和 www.example.net 会导致会话行为异常。

如果您将 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 的值更改为唯一的值,就完成了!
Nigel Barlass
17 年前
Lima 关于会话和浏览器选项卡的说明需要为我的 PHP 版本进行修改,因为对 uniqid('') 的调用将返回一个字母数字字符串。
因此,ereg 语句应为:
if(!ereg('^SESS[0-9a-z]+$',$_REQUEST['SESSION_NAME'])) {…
paul at shirron dot net
16 年前
在我的 php.ini 中,我有

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

我正在清理 temp 目录,并删除了 php 目录。会话功能停止工作。我重新创建了 php 目录。仍然没有用。我在 php 目录中重新创建了 session 目录,会话功能恢复正常。

我原本以为 session_start() 会在路径中重新创建目录(如果不存在),但事实并非如此。

给自己的一点提醒:不要再这样做了!
ted at tedmurph dot com
13 年前
我遇到了 $_SESSION 信息无法写入或以看似随机的方式丢失的问题。在 Zend OAuth 模块的深处有一个 Location: 调用,我使用的是带有 PHP 作为 CGI 的 IIS 服务器等等。

答案很简单,您需要确保会话工作的域保持一致。在我的情况下,我在 www.EXAMPLE.com:888 和 EXAMPLE.com:888 之间来回切换。不寻常的端口、隐藏的 Location: 调用、与 OAuth 的交互等等都让我感到困惑,但间歇性错误是由这个简单的错误造成的,即保持域一致。
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 程序员
法尔哈德·赞德·莫加达姆
edA-qa 在 disemia 点 com
15 年前
对于 Debian 用户的警告。只是为了让你彻底发疯,Debian 具有自己的会话管理形式,并且会完全忽略你 PHP 脚本中对这些值的所有更改。

Debian 设置了一个 <crontab /etc/cron.d/php5>,它会删除所有文件(包括子目录中的文件),这些文件超过了 <php.ini> 文件中指定的 gc_maxlifetime。

也就是说,在 Debian(以及 Ubuntu 等变体)上,修改会话过期设置(如 gc_maxlifetime)*没有任何作用*。你*必须*修改全局 <php.ini>。即使是 <.htaccess> 文件也无法帮助你。
特雷弗·布朗
14 年前
行为确认,以防万一这能节省其他人的时间…

当 session.gc_probability = 0 且 session.gc_divisor = 0 时,(可能)存在特殊情况。根据他们想要在其下工作的数学方式,编码人员*可能*选择将 0/0 解释为 1(这在某些数学证明中有时被假设),但幸运的是,他们似乎没有这样做。当这两个值都设置为 0 时,会话代码将遵循指令的精神,并以零概率调用垃圾收集器。至少对于 php 5.2.5(我检查了它的源代码以确保)来说是正确的。

但是,为了安全起见,将这两个值都设置为零*可能*不是一个好主意,因为通常 0/0 是未定义的,因此它可能意味着任何东西(有一些论点声称 0/0 等于每个分数)。你不希望确切地知道你的概率设定为多少吗?换句话说,不要将 gc_divisor 设置为 0
session 在 emailaddress d cjb d net
16 年前
它没有出现在文档中,也没有出现在这里的任何人的评论中,但将 session.gc_maxlifetime 设置为 0 意味着会话将不会过期,直到浏览器关闭。

当然,这仍然不能解决与垃圾收集器自行处理相关的问题。
对此的最佳解决方案似乎仍然是更改 session.save_path
jitchavan 在 gmail 点 com
13 年前
IE 问题 :-

当表单目标设置为 iframe 源,并且在发布表单内容后,你正在设置会话变量时,
在这种情况下,如果父页面具有空白的图像 src,那么在 iframe 操作页面中设置的会话值将令人惊讶地仅在 IE 中丢失。解决方案很简单,不要将 Image src 保持为空白。
jsnell 在 e-normous 点 com
16 年前
小心不要尝试将整数用作 $_SESSION 数组的键(例如 $_SESSION[0] = 1;),否则你会收到错误“Notice: Unknown: Skipping numeric key 0. in Unknown on line 0”
edA-qa 在 disemia 点 com
15 年前
会话可能在你设置的 gc_maxlifetime 时间限制之前被删除。

如果你在同一台服务器上有多个页面,每个页面都使用会话(相同或不同的命名会话,无关紧要),那么所有这些脚本中*最小*的 gc_maxlifetime 最终将成为会话文件的有效生命周期。

这也适用于在该机器上执行 PHP 脚本的 CLI 调用,该脚本恰好使用会话。

这可能很烦人,因为即使你认为所有页面都包含设置脚本的相同文件,但即使是一个没有使用会话的 PHP 页面也可以调用 GC 并删除脚本。

因此,如果你需要设置较长的 gc_maxlifetime,最好通过 INI 文件或整个目录的 .htaccess 文件来执行此操作。
shanemayer42 在 yahoo 点 com
23 年前
会话垃圾收集观察

似乎会话文件垃圾收集是在加载当前会话*之后*发生的。

这意味着
即使 session.gc_maxlifetime = 1 秒,
如果某人启动了会话 A,并且在 1 小时内没有人启动会话,那么这个人可以重新连接到会话 A,并且他们之前的所有会话值都将可用(也就是说,即使会话 A 超过了 gc_maxlifetime,它也不会被清除)。
Ray.Paseur 有时使用 Gmail
7 年前
PHP 5+ 中,你无法在会话中存储 StdClass 和类似的内置对象。类定义不存在,因此 PHP 无法序列化/反序列化信息。这会导致致命错误;会话的整个内容将丢失。

<?php
error_reporting
(E_ALL);
session_start();
var_dump($_SESSION); // 初始状态
$_SESSION['hello'] = 'World';
var_dump($_SESSION); // 更改后的状态

// 用于测试数据的 XML 字符串
$xml = <<<EOD
<?xml version="1.0"?>
<families>
<parent>
<child index="1" value="Category 1">Child One</child>
</parent>
</families>
EOD;

// 将 SimpleXMLElement 对象存储在会话中
$_SESSION['obj'] = SimpleXML_Load_String($xml);
Vextor
16 年前
似乎垃圾回收引擎无法删除与自身相关的已过期会话。如果只有一个会话,即使超过了设置的 gc_maxlifetime,它也不会过期。

需要另一个客户端连接,启动一个不同的会话,这个新会话的垃圾回收器才能清理其他已过期的会话。

我在 Windows 上使用文件会话测试了这一点。
To Top