我应该如何在 PHP 中实现惰性会话创建? [英] How should I implement lazy session creation in PHP?

查看:48
本文介绍了我应该如何在 PHP 中实现惰性会话创建?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

默认情况下,即使会话中没有数据,PHP 的会话处理机制也会设置会话 cookie 标头并存储会话.如果会话中没有设置数据,那么我不希望在响应中将 Set-Cookie 标头发送到客户端,并且我不希望在服务器上存储空会话记录.如果数据被添加到 $_SESSION,那么正常行为应该继续.

By default, PHP's session handling mechanisms set a session cookie header and store a session even if there is no data in the session. If no data is set in the session then I don't want a Set-Cookie header sent to the client in the response and I don't want an empty session record stored on the server. If data is added to $_SESSION, then the normal behavior should continue.

我的目标是实现 Drupal 7 和 Pressflow 类型的惰性会话创建行为,其中没有会话除非在应用程序执行期间将数据添加到 $_SESSION 数组,否则会存储(或发送会话 cookie 标头).这种行为的重点是允许诸如 Varnish 之类的反向代理缓存和提供匿名流量,同时允许经过身份验证的请求传递到 Apache/PHP.Varnish(或其他代理服务器)被配置为通过任何没有 cookie 的请求,正确假设如果 cookie 存在那么请求是针对特定客户端的.

My goal is to implement lazy session creation behavior of the sort that Drupal 7 and Pressflow where no session is stored (or session cookie header sent) unless data is added to the $_SESSION array during application execution. The point of this behavior is to allow reverse proxies such as Varnish to cache and serve anonymous traffic while letting authenticated requests pass through to Apache/PHP. Varnish (or another proxy-server) is configured to pass through any requests without cookies, assuming correctly that if a cookie exists then the request is for a particular client.

我已经从 Pressflow 移植了会话处理代码,它使用 session_set_save_handler() 并覆盖了 session_write() 的实现来检查 $_SESSION 中的数据 数组,然后将其写为库,如果这是最佳/唯一的途径,则在此处添加答案.

I have ported the session handling code from Pressflow that uses session_set_save_handler() and overrides the implementation of session_write() to check for data in the $_SESSION array before saving and will write this up as library and add an answer here if this is the best/only route to take.

我的问题:虽然我可以实现一个完全自定义的 session_set_save_handler() 系统,但是否有一种更简单的方法来以相对通用的方式获得这种惰性会话创建行为?对大多数应用程序透明吗?

My Question: While I can implement a fully custom session_set_save_handler() system, is there an easier way to get this lazy session creation behavior in a relatively generic way that would be transparent to most applications?

推荐答案

我开发了一个工作解决方案 这个问题使用 session_set_save_handler() 和一组自定义会话存储方法,这些方法在写出会话数据之前检查 $_SESSION 数组中的内容.如果会话没有数据要写入,则使用 header('Set-Cookie:', true); 来防止响应中发送 PHP 的 session-cookie.

I have developed a working solution to this problem that uses session_set_save_handler() and a set of custom session storage methods that check for content in the $_SESSION array before writing out session data. If there is no data to write for the session, then header('Set-Cookie:', true); is used to prevent PHP's session-cookie from being sent in the response.

此代码的最新版本以及文档和示例可在 GitHub 上获得.在下面的代码中,完成这项工作的重要函数是 lazysess_read($id)lazysess_write($id, $sess_data).

The latest version of this code as well as documentation and examples are available on GitHub. In the code below, the important functions that make this work are lazysess_read($id) and lazysess_write($id, $sess_data).

<?php
/**
 * This file registers session save handlers so that sessions are not created if no data
 * has been added to the $_SESSION array.
 * 
 * This code is based on the session handling code in Pressflow (a backport of
 * Drupal 7 performance features to Drupal 6) as well as the example code described
 * the PHP.net documentation for session_set_save_handler(). The actual session data
 * storage in the file-system is directly from the PHP.net example while the switching
 * based on session data presence is merged in from Pressflow's includes/session.inc
 *
 * Links:
 *      http://www.php.net/manual/en/function.session-set-save-handler.php
 *      http://bazaar.launchpad.net/~pressflow/pressflow/6/annotate/head:/includes/session.inc
 *
 * Caveats:
 *      - Requires output buffering before session_write_close(). If content is 
 *        sent before shutdown or session_write_close() is called manually, then 
 *        the check for an empty session won't happen and Set-Cookie headers will
 *        get sent.
 *        
 *        Work-around: Call session_write_close() before using flush();
 *        
 *      - The current implementation blows away all Set-Cookie headers if the
 *        session is empty. This basic implementation will prevent any additional
 *        cookie use and should be improved if using non-session cookies.
 *
 * @copyright Copyright &copy; 2010, Middlebury College
 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License (GPL), Version 3 or later.
 */ 

/*********************************************************
 * Storage Callbacks
 *********************************************************/

function lazysess_open($save_path, $session_name)
{
    global $sess_save_path;

    $sess_save_path = $save_path;
    return(true);
}

function lazysess_close()
{
    return(true);
}

function lazysess_read($id)
{ 
    // Write and Close handlers are called after destructing objects
    // since PHP 5.0.5.
    // Thus destructors can use sessions but session handler can't use objects.
    // So we are moving session closure before destructing objects.
    register_shutdown_function('session_write_close');

    // Handle the case of first time visitors and clients that don't store cookies (eg. web crawlers).
    if (!isset($_COOKIE[session_name()])) {
        return '';
    }

    // Continue with reading.
    global $sess_save_path;

    $sess_file = "$sess_save_path/sess_$id";
    return (string) @file_get_contents($sess_file);
}

function lazysess_write($id, $sess_data)
{ 
    // If saving of session data is disabled, or if a new empty anonymous session
    // has been started, do nothing. This keeps anonymous users, including
    // crawlers, out of the session table, unless they actually have something
    // stored in $_SESSION.
    if (empty($_COOKIE[session_name()]) && empty($sess_data)) {

        // Ensure that the client doesn't store the session cookie as it is worthless
        lazysess_remove_session_cookie_header();

        return TRUE;
    }

    // Continue with storage
    global $sess_save_path;

    $sess_file = "$sess_save_path/sess_$id";
    if ($fp = @fopen($sess_file, "w")) {
        $return = fwrite($fp, $sess_data);
        fclose($fp);
        return $return;
    } else {
        return(false);
    }

}

function lazysess_destroy($id)
{
    // If the session ID being destroyed is the one of the current user,
    // clean-up his/her session data and cookie.
    if ($id == session_id()) {
        global $user;

        // Reset $_SESSION and $user to prevent a new session from being started
        // in drupal_session_commit()
        $_SESSION = array();

        // Unset the session cookie.
        lazysess_set_delete_cookie_header();
        if (isset($_COOKIE[session_name()])) {
            unset($_COOKIE[session_name()]);
        }
    }


    // Continue with destruction
    global $sess_save_path;

    $sess_file = "$sess_save_path/sess_$id";
    return(@unlink($sess_file));
}

function lazysess_gc($maxlifetime)
{
    global $sess_save_path;

    foreach (glob("$sess_save_path/sess_*") as $filename) {
        if (filemtime($filename) + $maxlifetime < time()) {
            @unlink($filename);
        }
    }
    return true;
}

/*********************************************************
 * Helper functions
 *********************************************************/

function lazysess_set_delete_cookie_header() {
    $params = session_get_cookie_params();

    if (version_compare(PHP_VERSION, '5.2.0') === 1) {
        setcookie(session_name(), '', $_SERVER['REQUEST_TIME'] - 3600, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
    }
    else {
        setcookie(session_name(), '', $_SERVER['REQUEST_TIME'] - 3600, $params['path'], $params['domain'], $params['secure']);          
    }
}

function lazysess_remove_session_cookie_header () {
    // Note: this implementation will blow away all Set-Cookie headers, not just
    // those for the session cookie. If your app uses other cookies, reimplement
    // this function.
    header('Set-Cookie:', true);
}

/*********************************************************
 * Register the save handlers
 *********************************************************/

session_set_save_handler('lazysess_open', 'lazysess_close', 'lazysess_read', 'lazysess_write', 'lazysess_destroy', 'lazysess_gc');

虽然此解决方案有效并且对包括它的应用程序大部分是透明的,但它需要重写整个会话存储机制,而不是依赖内置的存储机制,并通过开关来保存或不保存.

While this solution works and is mostly transparent to applications including it, it requires rewriting the entire session-storage mechanism rather than relying on the built-in storage mechanisms with a switch to save or not.

这篇关于我应该如何在 PHP 中实现惰性会话创建?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆