PHP Conference Japan 2024

session_start

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

session_start启动新会话或恢复现有会话

描述

session_start(数组 $options = []): 布尔值

session_start() 基于通过 GET 或 POST 请求传递的会话标识符,或通过 Cookie 传递的会话标识符,创建会话或恢复当前会话。

当调用 session_start() 或会话自动启动时,PHP 将调用 open 和 read 会话保存处理程序。这些将是默认情况下或由 PHP 扩展(例如 SQLite 或 Memcached)提供的内置保存处理程序;或者可以是 session_set_save_handler() 定义的自定义处理程序。read 回调将检索任何现有的会话数据(存储在特殊的序列化格式中),并将被反序列化并用于在 read 回调将保存的会话数据返回给 PHP 会话处理时自动填充 $_SESSION 超全局变量。

要使用命名会话,请在调用 session_start() 之前调用 session_name()

session.use_trans_sid 启用时,session_start() 函数将为 URL 重写注册一个内部输出处理程序。

如果用户使用 ob_gzhandler 或类似于 ob_start() 的函数,则函数顺序对于正确的输出很重要。例如,必须在启动会话之前注册 ob_gzhandler

参数

options

如果提供,则为一个关联数组,其中包含将覆盖当前设置的 会话配置指令 的选项。键不应包含 session. 前缀。

除了正常的配置指令集外,还可以提供 read_and_close 选项。如果设置为 true,这将导致会话在读取后立即关闭,从而避免在不更改会话数据的情况下进行不必要的锁定。

返回值

如果会话成功启动,则此函数返回 true,否则返回 false

变更日志

版本 描述
7.1.0 session_start() 现在返回 false,并且在无法启动会话时不再初始化 $_SESSION

示例

一个基本的会话示例

示例 #1 page1.php

<?php
// page1.php

session_start();

echo
'欢迎访问第 1 页';

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

// 如果接受了会话 Cookie,则有效
echo '<br /><a href="page2.php">第 2 页</a>';

// 或者,如果需要,可以传递会话 ID
echo '<br /><a href="page2.php?' . SID . '">第 2 页</a>';
?>

查看 page1.php 后,第二个页面 page2.php 将神奇地包含会话数据。阅读 会话参考 以获取有关 传播会话 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 读取会话并关闭它

<?php
// 如果我们知道不需要更改会话中的任何内容,
// 我们可以立即读取并关闭,以避免
// 锁定会话文件并阻止其他页面
session_start([
'cookie_lifetime' => 86400,
'read_and_close' => true,
]);

注释

注意:

要使用基于 Cookie 的会话,必须在向浏览器输出任何内容之前调用 session_start()

注意:

建议使用 zlib.output_compression 而不是 ob_gzhandler()

注意:

此函数根据配置发送多个 HTTP 标头。请参阅 session_cache_limiter() 以自定义这些标头。

参见

添加注释

用户贡献的注释 36 条注释

linblow at hotmail dot fr
13 年前
如果您想使用类来处理会话,我编写了这个小程序类



<?php

/*
使用静态方法 getInstance 获取对象。
*/

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

// 会话的状态
private $sessionState = self::SESSION_NOT_STARTED;

// 唯一的类实例
private static $instance;


private function
__construct() {}


/**
* 返回 'Session' 的实例。
* 如果会话未初始化,则自动初始化。
*
* @return object
**/

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

self::$instance->startSession();

return
self::$instance;
}


/**
* (重新)启动会话。
*
* @return bool 如果会话已初始化,则返回 TRUE,否则返回 FALSE。
**/

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

return
$this->sessionState;
}


/**
* 在会话中存储数据。
* 例如:$instance->foo = 'bar';
*
* @param name 数据的名称。
* @param value 您的数据。
* @return void
**/

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


/**
* 从会话中获取数据。
* 例如:echo $instance->foo;
*
* @param name 要获取的数据的名称。
* @return mixed 存储在会话中的数据。
**/

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


/**
* 销毁当前会话。
*
* @return bool 如果会话已删除,则返回 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();

// 在会话中存储数据
$data->nickname = 'Someone';
$data->age = 18;

// 显示数据
printf( '<p>我的名字是 %s,我 %d 岁了。</p>' , $data->nickname , $data->age );

/*
它将显示:

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

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

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

// 销毁会话
$data->destroy();

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

?>

我更喜欢使用这个类而不是直接使用数组 $_SESSION。
[email protected]
9 年前
正如其他人所指出的,PHP 的会话处理程序是阻塞的。当您的某个脚本调用 session_start() 时,任何其他也调用 session_start() 且具有相同会话 ID 的脚本都将休眠,直到第一个脚本关闭会话。

一个常见的解决方法是在每次想要更新会话时都调用 session_start() 和 session_write_close()。

此方法的问题在于,每次调用 session_start() 时,PHP 都会将会话 cookie 的副本打印到 HTTP 响应标头。如果这样做足够多次(例如在长时间运行的脚本中),响应标头可能会变得非常大,从而导致 Web 服务器和浏览器崩溃或拒绝您的响应,因为它格式不正确。

此错误已报告给 PHP 总部,但他们将其标记为“不会修复”,因为他们说您不应该像这样在一个脚本中打开和关闭会话。 https://bugs.php.net/bug.php?id=31455

作为解决方法,我编写了一个使用 headers_list() 和 header_remove() 来清除重复 cookie 的函数。有趣的是,即使在 PHP 发送重复会话 cookie 的请求中,headers_list() 仍然只列出会话 cookie 的一个副本。尽管如此,调用 header_remove() 仍然会删除所有重复的副本。

<?php
/**
* 每次调用 session_start() 时,PHP 都会将另一个
* 相同的会话 cookie 添加到响应标头。如果这样做
* 足够多次,您的响应标头将变得足够大
* 以致于使 Web 服务器窒息。
*
* 此方法清除重复的会话 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);
}
}
?>
[email protected]
10 年前
如果 php.ini 文件中的指令 session.use_trans_sid 设置为 0,则常量 SID 将始终为 ''(空字符串)。

因此,请记住在 php 脚本中使用 SID 之前,将 session.use_trans_sid 设置为 1 并重新启动服务器。
[email protected]
7 年前
我最近做了一个有趣的观察

似乎即使会话未正确创建,`session_start()` 也可以返回 `true`。在我的情况下,磁盘存储已满,因此会话数据无法写入磁盘。当会话未写入磁盘时,我的一些逻辑导致了无限循环。

为了检查会话是否真的已保存到磁盘,我使用了

```
<?php

function safe_session_start() {
# 尝试启动会话
if (!@\session_start()) return false;

#
# 检查是否需要执行
# 写入测试。
#
if (!isset($_SESSION['__validated'])) {
$_SESSION['__validated'] = 1;

# 尝试将会话写入磁盘
@\session_write_close();

# 从内存中取消设置变量。
# 此步骤可能没有必要
unset($_SESSION['__validated']);

# 重新启动会话
@\session_start();

# 检查变量值是否保留
if (!isset($_SESSION['__validated'])) {
# 会话未写入磁盘
return false;
}
}

return
true;
}

if (!
safe_session_start()) {
# 会话可能未写入磁盘...
# 相应地处理错误。
}

?>
```

我花了相当长的时间才弄明白这一点。

也许它可以帮助某些人!
bachtel at [googles email service]dotcom
7 年前
如果您正在通过 session_set_save_handler() 使用自定义会话处理程序,那么在 PHP 7.1 中调用 session_start() 可能会看到类似这样的错误
session_start(): Failed to read session data: user (path: /var/lib/php/session) in ...

截至撰写本文时,它似乎发生在 PHP 7.1 中,而 PHP7.0 中一切看起来都正常。

也很难追踪,因为如果此 ID 已经存在会话(可能由早期版本的 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;
}

?>
emre@yazici
15 年前
PHP 手册专门指出了这个常见错误

根据会话处理程序的不同,会话 ID 中并不允许所有字符。例如,文件会话处理程序仅允许范围 a-z A-Z 0-9、(逗号)和 -(减号)中的字符!

有关更多详细信息,请参阅 session_id() 手册页。
bwz
1 年前
请注意阻塞会话的另一个问题:如果您想调用需要使用相同会话访问您网站的外部程序(或使用外部服务)。

例如,我正在将页面打印为 PDF。我可以将网页另存为 HTML 文件。但是 HTML 中的图像也是私有的,需要查看当前用户的会话才能看到。

发生的情况是,此程序可能会无限期挂起(或超时),因为 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 来释放锁,当您需要写入会话时,再次执行相同的操作,但正如所指出的那样,它有自己的问题。使用自定义会话处理程序可能是最佳解决方案。
elitescripts2000 at yahoo dot com
10 年前
AJAX 应用中会话的 3 个简单但至关重要的方面。

<?php
// 会话开始

// 如果使用整个域名,则包括句点非常重要(.yourdomain.com)
// 设置会话始终运行的根路径非常重要...(/members)将确保会话不会被
// 路径为(/admin)的会话干扰...因此您可以以 /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 数组
// 接下来,大多数人没有做的是删除会话 cookie!
// 通过在当前时间之前很久使 cookie 过期,可以轻松删除 cookie。
// 删除 cookie 的唯一方法是确保所有参数都与
// 要删除的 cookie 匹配...这很容易使用
// session_get_cookie_params() 获取这些参数...
// 最后,按此顺序使用 session_unset(); 和 session_destroy(); 以确保
// Chrome、IE、Firefox 等正确销毁会话。
$_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 和会话。
// 例如...您启动一个基于会话的 PHP 页面,然后调用一个使用
// 相同会话进行身份验证的 Ajax (XMLHTTP) 以轮询和输出数据,例如。但是,您注意到当您
// 尝试启动轮询 AJAX 调用时,它总是挂起,并且似乎挂在 session_start() 上。
// 这是因为会话在第一个页面中打开,调用 AJAX 轮询示例,并且
// 尝试打开相同的会话(用于身份验证)并进行 AJAX 调用,您必须调用
// session_write_close(); 意味着您已完成对 $_SESSION 变量的写入,它实际上
// 表示必须使用 session_write_close(); 关闭的文件......
// 然后,您可以调用您的 AJAX 轮询代码以重新打开相同的会话并进行轮询...
// 通常,当脚本关闭或执行完毕时,$_SESSION 会自动关闭
// 因此,如果您需要在打开会话后使 PHP 页面保持运行,只需在完成
// 对 $_SESSION 的写入时关闭它,以便 AJAX 轮询页面可以在
// 单独的网页中进行身份验证并使用相同的会话...

session_write_close();

?>

希望这对某些人及其会话有所帮助……
谢谢。
dave1010 at gmail dot com
13 年前
PHP 会锁定会话文件,直到它关闭。如果您有两个脚本使用相同的会话(即来自同一用户),则第二个脚本必须等到第一个脚本执行完毕才能完成其对 session_start() 的调用。

如果您有运行时间超过一秒的脚本,并且用户可能一次发出多个请求,那么在完成会话数据写入后立即调用 session_write_close() 是值得的。

<?php
// 会话上会放置一个锁,因此其他脚本将不得不等待
session_start();

// 执行所有对 $_SESSION 的写入
$_SESSION['a'] = 1;

// 仍然可以读取 $_SESSION,但写入不会更新会话。
// 锁被移除,其他脚本现在可以读取会话
session_write_close();

do_something_slow();
?>

http://konrness.com/php5/how-to-prevent-blocking-php-requests/ 中了解到这一点
Anonymous
4 年前
小心使用“read_and_close”选项。与默认的 PHP 行为(当您不关闭会话或显式使用 session_write_close 时)不同,它不会更新会话文件的最后修改时间。
旧的会话文件(对我来说,超过 24 分钟的旧文件)偶尔会被垃圾回收器清除(对我来说,每小时的第 09 分和第 39 分)。
因此,即使页面定期向服务器发送仅读取和关闭会话的请求,会话也可能消失。
ben dot morin at spaboom dot com
18 年前
skinsupport dot com 上的 James 提出了一个很好的观点(警告),关于来自浏览器的额外请求。favicon.ico 的请求,根据其处理方式,可能会对您的会话产生意外的结果。



例如,假设您有 ErrorDocument 404 /signin.php,没有 favicon.ico 文件,并且您网站中所有用户登录的页面在用户尚未登录的情况下也会重定向到 /signin.php。

如果 signin.php 执行任何清理或重新分配 session_id 的操作(所有良好的 signin.php 页面都应该这样做),那么浏览器对 favicon.ico 的额外请求可能会损坏实际请求设置的会话。

感谢 James 指出这一点,也为我草率地浏览而没有注意到它如何应用于我的问题而感到羞愧。也感谢 Firefox Live HTTP Headers 扩展程序显示了额外的请求。

如果您的会话 Cookie 未被发送或会话数据与您期望的不同,请不要浪费几天甚至几个小时的时间。至少,消除这种情况,并查看是否有任何其他请求可能存在问题。
someOne_01 at somewhere dot com
12 年前
当您有一个需要很长时间才能执行的导入脚本时,浏览器似乎会锁定,您无法再访问网站。这是因为一个请求正在读取并锁定会话文件以防止损坏。

您可以:
- 使用 session_set_save_handler() 使用不同的会话处理程序
- 在导入脚本中使用 session_write_close(),只要您不再需要会话(最佳时机是在长时间运行的部分开始之前),您可以在任何时候以及您喜欢的次数启动会话,如果您的导入脚本需要更改会话变量。

示例
<?php
session_start
(); //初始化/打开会话
$_SESSION['count'] = 0; //在会话中存储一些内容
session_write_close(); //现在关闭它,
# 从这里可以运行任何其他脚本(并使其看起来像多任务处理)
for($i=0; $i<=100; $i++){ //执行 100 个循环
session_start(); //再次打开会话以编辑变量
$_SESSION['count'] += 1; //更改变量
session_write_close(); //现在再次关闭会话!
sleep(2); //每个循环休眠两秒,或执行繁重的任务
}
?>
James
18 年前
为了避免 PHP 从 4.3.3 开始两次启动会话时提交的通知,请先检查 session_id()

if (session_id() == "")
session_start();
jamestrowbridge at gmail dot com
14 年前
不幸的是,在尝试解决为什么我的应用程序在除 IE(Internet Explorer)之外的所有浏览器中都能正常工作(我在 Opera、Chrome、Firefox、Safari 中测试过)时,当我使用 DNS CNAME 记录(如与 DNS A 记录不同的虚名,即服务器的主机名)时,会话无法正常工作。

如果您在 CNAME 上存储会话变量
vanity.example.com 并且服务器的主机名为 hosname.example.com
然后尝试从不同的页面调用该变量,它将找不到它,因为 CNAME(我猜它在主机名下存储了变量,然后在尝试读取它时它仍在 CNAME 下查找)相同的应用程序在直接在主机名下访问它时可以正常工作。请记住,我在内部网络上测试了这一点。
hu60 dot cn at gmail dot com
5 年前
以下代码显示了 PHP 会话的工作原理。函数 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;
}
[email protected]
19年前
如果您使用javascript window.open打开一个弹出窗口(请勿使用商业弹出窗口!),IE可能会阻止会话cookie。
一个简单的解决方法是在打开新窗口时,在GET值中包含会话ID。请注意,我没有使用SID,因为它并不总是可用。

----page.php----
//您必须在此处激活一个会话
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();
//然后继续使用您的会话变量
?>
[email protected]
7 年前
X维护人员...抱歉如此烦人,请删除此重复项,因为是在一个疯狂的“会话”中提交的,我在浏览器选项卡之间搞砸了一些东西...再次抱歉,alessio
https://php.net/manual/en/function.session-start.php#121310
schlang
15 年前
如果您将会话存储在数据库中,请始终确保数据库列的类型足够大以容纳您的会话值。
[email protected]
16年前
使用session_start创建的会话仅对首次创建该会话的页面所在的目录树中的页面可用。

例如,如果首次创建会话的页面是/dir1/dir2/index.php,然后用户转到dir2上方的任何页面(例如/dir1/index.php),session_start将创建一个新的会话,而不是使用现有的会话。
[email protected]
11年前
关于session_start()、自定义处理程序和数据库外键约束的说明,我认为可能有些用处...

我们知道,如果我们希望将会话放入数据库表(而不是默认存储),我们可以参考session_set_save_handler(...) 将其存储到数据库中。请注意,session_set_save_handler必须(显然)在session_start()之前调用,但让我说明重点...

在“第一次”调用session_start()时,当会话尚不存在时,php将生成一个新的会话,但直到脚本执行结束才会调用写入处理程序。

因此,此时会话存在于服务器进程内存中,但在脚本结束之前不会在数据库中显示为一行。

这似乎是合理的,因为它避免了在我们将会话填充有意义和确定性数据之前进行一些不必要的数据库访问和资源使用,但这也有一些副作用。

在我的情况下,脚本调用session_start()以确保会话已启动,然后使用session_id()填充数据库中的另一个表,该表对“会话”表具有外键约束。这失败了,因为此时数据库中还没有会话!

我知道我可以通过在必要时手动调用写入处理程序来强制在数据库中创建行,但这不确定是否是最佳方法。

一旦我找到一个“优雅”的解决方案或完全不同的方法,我将发布一些可工作的示例代码。

在此期间...玩得开心!
[email protected]
7 年前
初始化会话可能会覆盖您自己的自定义缓存控制标头,这可能会导致点击后退按钮返回之前的帖子请求(至少在Chrome上是这样)。
在我的系统上,它设置为“no-store”,这比“no-cache”严重得多,并且导致后退按钮失效。

如果您正在仔细控制自己的缓存标头,则需要调用
session_cache_limiter('');

...以阻止它更改您的缓存控制标头。
[email protected]
8年前
如果您需要在同一个脚本中打开多个不同的会话,并且仍然让PHP为您生成会话ID,这里有一个我想到的简单函数(假设使用PHP默认会话处理程序)

<?php
/**
* 切换或透明地创建名为 $name 的会话。
* 它可以很容易地扩展以管理不同的会话生命周期。
*/
function session_switch($name = "PHPSESSID") {
static
$created_sessions = array();

if (
session_id() != '') { // 如果当前已打开一个会话,则关闭它
session_write_close();
}
session_name($name);
if (isset(
$_COOKIE[$name])) { // 如果特定会话已存在,则与 $created_sessions 合并
$created_sessions[$name] = $_COOKIE[$name];
}
if (isset(
$created_sessions[$name])) { // 如果会话已存在,则模拟它
session_id($created_sessions[$name]);
session_start();
} else {
// 创建新会话
session_start();
$_SESSION = array(); // 在复制会话文件之前清空内容
// 使用新的 ID 和当前 $_SESSION 内容复制最后一个会话文件
// 如果这是第一个创建的会话,则没有内容可以复制,并且将 true 作为参数传递将负责“仅”创建单个会话文件
session_regenerate_id(empty($created_sessions));
$created_sessions[$name] = session_id();
}
}

session_switch("SESSION1");
$_SESSION["key"] = "value1"; // 特定于会话 1
session_switch("SESSION2");
$_SESSION["key"] = "value2"; // 特定于会话 2
session_switch("SESSION1");
// 返回会话 1
// ...
?>

使用此函数时,不应再单独调用 session_start()(可以用不带参数的 session_switch() 调用替换)。
还要记住,session_start() 在每次调用时都会设置 Set-Cookie HTTP 标头,因此,如果您在会话之间进行回显,请使用输出缓冲区包装。

注意:处理多个会话可能很少是一个好主意,因此如果您认为自己有很好的用途,请再三考虑。
就我个人而言,它在我的某些需要维护的遗留代码的快速修补中发挥了作用。
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 是否存在的实用程序。
polygon dot co dot in at gmail dot com
26 天前
<?php
session_start
([
'read_and_close' => true,
]);
?>

我相信 read_and_close 选项是作为只读模式会话来工作的

令人惊讶的是,即使 http 请求中不存在 sessionId cookie,它也能正常工作并生成一个新的 sessionId。

其次,使用此选项的函数还会根据新生成的 sessionId 生成新的会话文件

第三,如果 sessionId 无效(与 sessionId 对应的文件不存在),则会创建一个新的。
theking2(at)king.ma
5 个月前
我通常使用它来启动一个新会话

<?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'],
] );
?>

这将创建一个会话,并在会话 cookie 名称中增加熵,需要安全的会话,确保流氓引用没有机会访问我的登录页面等等。

根据 MDN[1],必须为安全会话设置前缀“__Secure-”。

[1](https://mdn.org.cn/en-US/docs/Web/HTTP/Headers/Set-Cookie)
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();
Charlie at NOSPAM dot example dot com
15 年前
请注意,根据脚本结束来关闭会话将有效地序列化并发会话请求。期望并行检索数据的并发后台“数据检索”(例如 AJAX 或 amfphp/Flex 等应用程序)很容易陷入此陷阱。

在昂贵的操作之后保持 session_write_close 也存在问题。

为了最大程度地减少影响,请尽早(例如,在不引入竞争条件的情况下)调用 session_write_close(又名 session_commit),或者避免序列化瓶颈。
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();
polygon dot co dot in at gmail dot com
3 年前
网站容易受到会话攻击,如果其使用不当。

有一些工具,例如“Apache Benchmark”(ab)以及许多其他工具,可以对网站进行负载/性能测试。

下面的代码为每个请求启动会话。

<?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 这样的工具时,这会为每个请求生成会话文件,而不管 PHPSESSID cookie 值如何,从而导致 inode 问题。

应该在正确身份验证后启动会话。

<?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');
}
?>

除了登录脚本之外,其他脚本首先验证会话,而会话需要先启动。

<?php

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

session_start();

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

下面的代码逻辑....
}
?>

此示例适用于基于文件的会话。
对于其他会话模式,请查看函数 session_set_save_handler。
anon at ymous dot com
13 年前
我试图获取浏览器调用创建的会话,以便通过命令行 cli->curl php 调用来使用(在本例中,这两个调用都指向同一服务器和 php.ini),用于一组灵活的媒体导入例程,

但是 cli->curl 调用始终会启动一个新的会话,即使我将 PHPSESSID=validID 作为 curl 调用的 URL 的第一个参数。

我能够通过在通过 curl 调用的脚本中调用 session_start() 之前调用 session_id($_GET['PHPSESSID']) 来解决此问题。
sanjuro at 1up-games dot com
13 年前
SID 的问题在于,如果在某些情况下您没有启动会话,则它不会输出空字符串以实现透明集成,而是会返回常规的未定义常量通知。因此,您可能希望事先使用 defined() 测试该常量。
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 . "/")) {
// 需要包含完整的“路径”,否则会无限循环
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(".");
?>
dstuff at brainsware dot org
15 年前
看起来名称中的空格也不起作用 - 每次都会生成新的会话 ID
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)
leandroico---at---gmail---dot---com
17 年前
标签:session_start 标头 输出错误 include_once require_once php 标签 新行

与在 include 文件中调用 *session_start()* 相关的输出标头错误。

如果您在 include 文件中启动会话,则必须注意 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;
?>

有了所有这些内容,我们将收到一个错误,类似于
"...无法发送会话缓存限制器 - 标头已发送(输出始于...”,对吧?

要解决此问题,我们必须忽略 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
"哇哦!没错!去死吧,你们这些不需要的输出!";
?>
james at skinsupport dot com
18 年前
有一件事需要注意,它让我困扰了三天

需要注意的是,Firefox(例如)会自动向服务器发出两个调用。一个用于页面,另一个用于 favicon.ico。

如果您在页面存在时将会话变量(就像我一样)设置为某些值,而在页面不存在时设置为其他值,则如果 favicon.ico 不存在,则不存在页面的值将覆盖存在页面的值。

我怀疑你们中很少有人这样做,但如果您正在这样做,那么这是您需要解决的问题,否则您将在三天的过程中变得秃顶!
To Top