首页 > 解决方案 > session_write_close():无法使用用户定义的保存处理程序写入会话数据。PHP 7.3.1

问题描述

我编写了一个自定义会话处理程序类,它在大多数情况下都有效,直到我尝试注销我的用户并销毁会话,然后我不断收到以下错误:

ErrorException: 0

session_write_close(): Failed to write session data using user defined save handler. (session.save_path: D:\xampp\tmp)

我在 XAMPP 上运行 PHP 版本 7.3.1。

这是我的自定义课程

class CornerstoneSessionHandler implements SessionHandlerInterface {

    /**
     * Construct the Session
     * No parameters required, nothing will be returned
  */
  public function __construct() {

    // Set the handler to overide SESSION
    session_set_save_handler(
      array($this, "open"),
      array($this, "close"),
      array($this, "read"),
      array($this, "write"),
      array($this, "destroy"),
      array($this, "gc")
      );

      // Set the shutdown function
      register_shutdown_function('session_write_close');

      /** Define and initialise the Session Handler */
      session_start();
  }

    /**
     * Set the open callback
     *
     * @param string $savePath
     * @param string $sessionName
   *
   * @return bool return value should be true for success or false for failure
  */
  public function open($savePath, $sessionName) {

        // Make the $csdb global accessible
        global $csdb;

    // Check that the DB connection is set
    return ((!empty($csdb)) && $csdb->isConnected() != 1) ? FALSE : TRUE ;
  }

    /**
     * Set the close callback
   *
   * @return bool return value can only be true for success
  */
  public function close() {
    return TRUE;
  }

    /**
     * Set the read callback
     *
     * @param string $sessionID
   *
   * @return string return value should be the session data or an empty string
  */
  public function read($sessionID) {

        // Make the $csdb global accessible
        global $csdb;

    // Get the session from the database
    $csdb->query_prepared("SELECT session_data FROM cs_session WHERE session_id=?", [$sessionID]);

    // If results returned, continue
        if($csdb->getNum_Rows() > 0) {

            // Get the data
            $result = $csdb->get_row(NULL);
      return $result->session_data;

    } else { // Else return an empty string

      return '';

    }
  }

    /**
     * Set the write callback
     *
     * @param string $sessionID
     * @param string $data
   *
   * @return bool return value should be true for success or false for failure
  */
  public function write($sessionID, $data) {

        // Make the $csdb global accessible
        global $csdb;

    // Set the time stamp
    $access_dtm = new \DateTime();

    // Replace the data
    $csdb->query_prepared('REPLACE INTO cs_session(session_id, session_ip_address, session_data , session_access_dtm) VALUES (?, ?, ?, ?)', [$sessionID, $_SERVER['REMOTE_ADDR'], $data, $access_dtm->format('Y-m-d H:i:s')]);

    // If modified a success, return true
        if($csdb->getNum_Rows() > 0) {
      return TRUE;
    } else { // Else, return false
      return FALSE;
    }
  }

    /**
     * Set the destroy callback
     *
     * @param string $sessionID
   *
   * @return bool return value should be true for success or false for failure
  */
  public function destroy($sessionID) {

        // Make the $csdb global accessible
    global $csdb;

    // Delete the session from the database
    $csdb->delete('cs_session', where(eq('session_id', $sessionID)));

    // If results returned, return true
        if($csdb->affectedRows() > 0) {
      return TRUE;
    } else { // Else, return false
      return FALSE;
    }
  }

    /**
     * Set the garbage collector callback
     *
     * @param string $lifetime
   *
   * @return bool return value should be true for success or false for failure
  */
  public function gc($lifetime) {

        // Make the $csdb global accessible
        global $csdb;

    // Set the date calculation
    $expiredTime = new \DateTime();
    $expiredTime->modify('-' . $lifetime . ' seconds');

    // Get the session from the database
    $csdb->delete('cs_session', where(lt('session_access_dtm', $expiredTime->format('Y-m-d H:i:s'))));

    // If results deleted, return true
        if($csdb->affectedRows() > 0) {
      return TRUE;
    } else { // Else, return false
      return FALSE;
    }

  }
}

我使用ezSQL作为我的数据库处理程序。

这是我所有页面顶部的代码

/**
 * Set session data so that logins work properly over all browsers
 * This should fix login errors some users can face
 * More info can be found at {@link https://www.php.net/manual/en/session.security.ini.php the php user manual}
 */
# PREVENTING SESSION HIJACKING
# Prevents javascript XSS attacks aimed to steal the session ID
ini_set('session.cookie_httponly', 1);
# Make sure the cookie lifetime is set to '0'
ini_set('session.cookie_lifetime', 0);
# Adds entropy into the randomization of the session ID, as PHP's random number
# generator has some known flaws
ini_set('session.entropy_file', '/dev/urandom');
# Uses a strong hash
ini_set('session.hash_function', 'whirlpool');
# Set the session save location (best for shared servers)
# Uncomment out the next line if you would like to set a custom path and haven't already set the value in your `php.ini` file.
# ini_set('session.save_path',realpath(ABSPATH . 'tmp' . DIRECTORY_SEPARATOR));
# Note: The folder referenced above must exist for it to work.
# Set the session garbage collection lifetime to custom defined minutes (PHP default is 24 minutes)
ini_set('session.gc_maxlifetime', (int)get_option("session_expire") * MINUTE_IN_SECONDS);
# Enable session garbage collection with a 1% chance of
# running on each session_start()
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 100);
# Uses a secure connection (HTTPS) if possible
# Allow cookies to be sent over insecure connections if not an HTTPS site
(SITE_HTTPS) ? ini_set('session.cookie_secure', 1) : ini_set('session.cookie_secure', false);
# PREVENTING SESSION FIXATION
# Session ID cannot be passed through URLs
# so only use cookies to store the session id on the client side
ini_set('session.use_only_cookies', 1);
# Set a custom session name
session_name('CSSESSID');
# Load the session class
new CornerstoneSessionHandler();
# Start the session if it's not already started
if (session_status() == PHP_SESSION_NONE) {session_start();}

奇怪的是,即使它给了我那个错误,如果我检查数据库,数据正在更新,不用担心。

这是我的注销代码

/**
 * Logout the user
 *
 * @return bool
*/
public static function logoutUser() {

    // Make the $fear global accessible
    global $fear; // Direct access check

    // Check if session set (just in case) and start if it isn't
    if(session_id() == '') {session_start();}

    // Delete the $_SESSION data set in `authenticateUser()`
    unset($_SESSION['HTTP_USER_AGENT']);
    unset($_SESSION['_cs-uid']);
    unset($_SESSION['_cs-ue']);
    unset($_SESSION['_cs-ul']);
    unset($_SESSION['_cs-un']);

    /**
     * Get the "ext.auth.php" file and run `clearCustomAuth()` function
     * to clear any custom set $_SESSION items
    */
    require_once( get_lib_path('ext.auth.php') );
    clearCustomAuth();

    // Regenerate a new session ID just to be sure
    session_regenerate_id();

    // Destroy the session
    session_destroy();

    // Check if the $_COOKIE data if set
    if(isset( $_COOKIE['_cs-ti'] ) && !empty( $_COOKIE['_cs-ti'] )) {
        // Delete the cookie token from the database
        if(!self::deleteAuthCookie($_COOKIE['_cs-ti'])) {
            // Return false if cookie couldn't be deleted
            return false;
        }
    }

    // Return true if run all the way
    return true;
}

我尝试过转移,session_regenerate_id()但是如果我把它放在它之后session_destroy()它说没有要重新生成的会话,但是如果我把它放在它之前session_destroy()或者根本没有它,我的会话将从我的数据库中删除但我仍然可以看到它在我的 Firefox 检查器中的存储中。

尝试从数据库中删除会话时,它只会给我一个错误。其他一切正常,不用担心!我在测试中手动尝试从数据库中删除一个会话,但我仍然收到该错误。

有没有人能够看到我可能做错了什么或如何解决它?我刚刚才知道这对它来说非常新。如果我需要提供更多信息,请告诉我。我已经在网上搜索了将近 2 天,但没有运气。

标签: phpsession

解决方案


我看不出您需要设置关闭处理程序的原因。当 php 关闭时,会话会自动保存,即使您遇到致命错误也是如此。

即使没有更新/删除行,write、destroy 和 gc 也应该始终返回 true。这似乎违反直觉,但 php 认为删除不存在的会话也是成功的。


推荐阅读