PHP Conference Japan 2024

会话函数

目录

添加注释

用户贡献的注释 20 条注释

14
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() 将自动将一对“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() 将定义脚本将使用的实际会话的名称。

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

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

当然,文档中确实提到了这一点(“传递会话 ID”),并且当然限制这样做是有道理的,但这就是我遇到的情况
我使用会话已经有一段时间了,一直没有问题。当我使用一个全局配置文件包含在我的所有脚本中时,其中包含这样一行

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

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

跳过“http:”解决了问题。

好吧,当然是我的错误,但这只是说明了人们多么容易地就能让自己工作几个小时都无法进行……千万不要这样做;)
5
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 . ' 已被 "unseted"';
}else{
echo
'<br>';
echo
$sessName . ' 未被 "unseted"';
}

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

在脚本执行后(即在 __destruct 函数中)设置会话变量将不起作用。

<?php

class Example{

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

}

?>

如我通过自定义会话保存处理程序观察到的,上述示例不会将任何内容写入临时会话文件。
4
Jeremy Speer
14 年前
在开发一个项目时,我发现需要在两个不同的软件之间切换活动会话。执行此操作的文档散布在不同的网站上,尤其是在评论部分而不是示例中。我遇到的一个困难是其中一个应用程序的会话保存处理程序已设置,而另一个则没有。现在,我没有在函数 session_set_save_handler() 中编写代码,而是在我完成函数后(手动)使用它,但是此函数可以轻松地扩展以包含该功能。基本上,它只是覆盖了系统默认的会话保存处理程序。在使用 getSessionData() 后克服此问题,只需调用 session_write_close()、session_set_save_handler() 并使用适当的值,然后使用其适当的值重新运行 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;
}
?>
1
hinom06 [at] hotmail.co.jp
14 年前
简单的会话测试版本 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 . ' 已 "解除设置"';
}else{
echo
'<br>';
echo
$sessName . ' 未 "解除设置"';
}

}
}
?>
<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>
0
brfelipe08 at hotmail dot com
15 年前
如果您需要使用会话,并且某些基于拉丁语系的语言需要一些重音符号,则应使用 ISO-8859-1 编码您的文件。
如果您尝试使用 UTF-8(带或不带 BOM),或者 ANSI 将不支持重音符号,则会遇到一些问题。
ISO-8859-1 将同时支持会话和重音符号。
-1
carl /a/ suchideas /o/ com
17 年前
此列表中需要添加另一个需要注意的地方是,使用相对的 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}
-1
LaurentT
16 年前
对于 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 的值更改为唯一的值即可!
-1
Nigel Barlass
17 年前
Lima 关于会话和浏览器选项卡的注释需要针对我的 PHP 版本进行修改,因为对 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"

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

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

给自己留个笔记:不要再这样做!!!!
-2
ted at tedmurph dot com
14 年前
我在 $_SESSION 信息没有写入或以看似随机的方式丢失方面遇到了问题。在 Zend OAuth 模块的深处进行了 Location: 调用,我正在使用带有 PHP 作为 CGI 的 IIS 服务器等。

答案很简单,您需要使会话的域保持一致才能使其正常工作。在我的情况下,我一直在 www.EXAMPLE.com:888 和 EXAMPLE.com:888 之间切换。不寻常的端口、隐藏的 Location: 调用、与 OAuth 的交互等都让我感到困惑,但间歇性错误是由这个简单的错误造成的,即保持域的一致性。
-2
farhad dot pd at gmail dot com
7 年前

<?php
Session
{

public static
$seesionFlashName = '__FlashBack';
/**
* [__construct 描述]
*/
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(
'错误: 无效的会话 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 描述]
* @param [type] $name [描述]
* @return [type] [描述]
*/
public static function exists($name) {
if(isset(
$name) && $name != '') {
if(isset(
$_SESSION[$name])) {
return
true;
}
}

return
false;
}

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

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

return
false;
}

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

return
false;
}

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

/**
* [getFlash 描述]
* @return [type] [描述]
*/
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 描述]
* @return [type] [描述]
*/
public static function flashExists() {
return
self::exists(self::$seesionFlashName);
}

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

session_destroy();
}
}
?>

伊朗 PHP 程序员
法尔哈德·赞德·莫加达姆
-2
[email protected]
15 年前
针对 Debian 用户的警告。仅仅是为了让你完全抓狂,Debian 有自己的会话管理方式,并且会完全忽略你在 PHP 脚本中对值所做的所有更改。

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

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

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

然而,为了安全起见,将这两个值都设置为零*可能*不是一个好主意,因为通常 0/0 是未定义的,因此它可能意味着任何东西(一些论点声称 0/0 等于每个分数)。你难道不想确定你的概率设置为什么吗?换句话说,不要将 gc_divisor 设置为 0。
-3
[email protected]
16 年前
在文档或任何人的评论中都没有提到,但是将 session.gc_maxlifetime 设置为 0 意味着会话在浏览器关闭之前不会过期。

当然,这仍然不能解决与垃圾回收器自行执行操作相关的问题。
解决该问题的最佳方法似乎仍然是更改 session.save_path。
-5
[email protected]
13 年前
IE 问题:-

当表单目标设置为 iframe 源,并且在发布表单内容后设置会话变量时,
在这种情况下,如果父页面具有空白的 image src,则在 iframe 操作页面中设置的会话值会在 IE 中意外丢失。解决方案很简单,不要将 Image src 保留为空白。
-5
[email protected]
15 年前
会话可能在你在 gc_maxlifetime 中设置的时间限制之前被删除。

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

这同样适用于来自该机器上 PHP 脚本的 CLI 调用(恰好使用会话)的生命周期。

这可能很麻烦,因为即使你认为所有页面都包含设置脚本的相同文件,即使单个未包含该文件的 PHP 页面也可以调用 GC 并删除脚本。

因此,如果你需要设置较长的 gc_maxlifetime,最好通过 INI 文件或整个目录的 .htaccess 文件来执行此操作。
-4
[email protected]
24 年前
会话垃圾回收观察

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

这意味着
即使 session.gc_maxlifetime = 1 秒,
如果有人启动了会话 A,并且在 1 个小时内没有人启动任何会话,则该人可以重新连接到会话 A,并且他们之前的所有会话值都将可用(也就是说,即使会话 A 比 gc_maxlifetime 旧,也不会被清除)。
To Top