自定义PHP SessionHandler类写问题

时间:2014-10-29 16:44:11

标签: php postgresql session nginx

我创建自己的自定义SessionHandler,将会话信息存储在 postgresql 9.3 数据库中,并且我遇到了会话数据传递给{{1}的问题方法没有被写入数据库,但会话名称是??

我知道的事实

  • 我的自定义类在调用write()时处理会话 - 使用各种方法的回显输出进行测试,并且/ tmp中没有创建会话文件
  • session_start()中的$session_data arg包含正确的序列化字符串,如回显write()方法中的内容所示。
  • write()正好被写入数据库,因此是一个BLANK序列化字符串$session_name

我感到困惑的事情:

  • 回显$ _SESSION [' test_var1']的内容会显示存储的正确值,即使a:0:{}为空还是没有返回值?
  • 如果会话名称保存在数据库中就好了,为什么不是会话数据呢?

服务器配置

  • OS:Gentoo
  • 数据库:Postgresql 9.3
  • Web服务器:NGINX 1.7.6
  • PHP 5.5.18通过FPM连接到NGINX

PHP ini会话设置

  • read()
  • session.save_handler = user
  • session.use_strict_mode = 1
  • session.use_cookies = 1
  • session.cookie_secure = 1
  • session.use_only_cookies = 1
  • session.name = _g
  • session.auto_start = 0

    session.serialize_handler = php_serialize

测试代码:

class SessionManagement implements SessionHandlerInterface {

private $_id = '';
private $_link = null;

public function __construct() {
   session_set_save_handler(
    [$this, 'open'],
    [$this, 'close'],
    [$this, 'read'],
    [$this, 'write'],
    [$this, 'destroy'],
    [$this, 'gc']
   );
}

public function open($save_path, $session_id) {
  echo 'open called<br/>';

  $this->_id = $session_id;
  $this->_link = new PDO('pgsql:host=' . $_SERVER['DB_HOST'] . ';dbname=' . $_SERVER['DB_DB'],
    $_SERVER['DB_USER'],
    $_SERVER['DB_PASS']);
}


public function close() {
  echo 'close called<br/>';
}

public function destroy($session_id) {
  echo 'destroying '.$session_id, '<br/>';

}

public function gc($maxlifetime) {
  echo 'GC called<br/>';

}

public function read($session_name) {
  $name = $this->_id.'_'.$session_name;
  $sql = 'SELECT session_data FROM sessions WHERE session_name = :name';
  if ($rel = $this->_link->prepare($sql)) {
    if ($rel->execute([':name' => $name])) {
      return $rel->fetch(PDO::FETCH_ASSOC)['session_data'];
    } else {
      return false;
    }
  } else {
    return false;
  }


  return '';
}


public function write($session_name, $session_data) {
  echo 'Session data: '.$session_data.'<br/>';
  $name = $this->_id . '_' . $session_name;
  $data = $session_data;
  $sql  = "SELECT 1 FROM sessions WHERE session_name = :name";

  if ($rel = $this->_link->prepare($sql)) {
    if ($rel->execute([':name' => $name])) {
      if ($rel->rowCount()) {
        echo 'Updating...<br/>';
        $sql = 'UPDATE sessions SET session_data = :data WHERE session_name = :name';
        if ($rel = $this->_link->prepare($sql)) {
          if ($rel->execute([':name' => $name, ':data' => $data])) {
            echo 'Update success...<br/>';
          } else {
            echo 'Update failed...<br/>';
            var_dump($rel->errorInfo());
          }
        }
      } else {
        echo 'Inserting...<br/>';
        $sql = 'INSERT INTO sessions (session_name, session_data) ';
        $sql .= 'VALUES(:name, :data)';
        if ($rel = $this->_link->prepare($sql)) {
          if ($rel->execute([':name' => $name, ':data' => $data])) {
            echo 'Insert success...<br/>';
          } else {
            echo 'Insert failed...<br/>';
            var_dump($rel->errorInfo());
          }
        }
      }
    }
  }
}
}

通过测试页输出

 new SessionManagement();

 session_start();

 $_SESSION['test_var1'] = 'some test data';
 session_write_close(); // Making sure writing is finished
 echo $_SESSION['test_var1'];

相关数据库字段

  • session_name:open called Session data: a:1:{s:9:"test_var1";s:14:"some test data";} Inserting... Insert success... close called some test data
  • session_data:_g_h8m64bsb7a72dpj56vgojn6f4k3ncdf97leihcqfupg2qtvpbo20

我不确定这是数据库问题还是PHP问题。我现在已经搞砸了几天,并决定是时候问社区了。希望有人能够了解问题所在。

2 个答案:

答案 0 :(得分:1)

我认为你必须在Open函数处理程序和类本身之外初始化PDO对象

尝试使用全局值或静态变量访问您的PDO对象。

这是我为我的项目使用MYSQL的实现:

class Core_DB_SessionHandler implements SessionHandlerInterface
{
    protected $options = array(); // Options de la session
    protected static $db = NULL; // Acceder a la BDD
    public function __construct($options, $pdo) {
        $this->options = $options;
        self::$db      = $pdo;
    }


    public function open($savePath, $sessionName) {         
        $now = time();
        $req = self::$db->prepare("DELETE FROM tbl_sessions WHERE expire < '{1}' ");
        $req->execute(array($now));
        return TRUE;
    }


    public function close() {
        $this->gc(ini_get('session.gc_maxlifetime'));
    }

    public function read($id) {
        $now = time();
        $stmt = self::$db->query("SELECT data FROM tbl_sessions WHERE sid = '$id AND expire < '$now'");
        $result = $stmt->fetchColumn();
        return $result;
    }


    public function write($id, $data) {
        if (array_key_exists('TIMEOUT', $_SESSION)) {
            $newExp = $_SESSION['TIMEOUT'];
        }
        else {
            $newExp = time() + $this->options['time_limiter'];
        }
        try {
            $req  = self::$db->prepare('INSERT INTO tbl_sessions (sid, data, expire) VALUES (:sid, :data, :expire)
        ON DUPLICATE KEY UPDATE data = :data, expire = :expire');
            $vals = array('sid' => $id, 'data' => $data, 'expire' => $newExp);          
            $req->execute($vals);
            return TRUE;
        }
        catch (PDOException $e) {
            throw new Core_Exception(sprintf('PDOException was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e);
        }
    }

    public function destroy($id) {          
        $stmt = self::$db->prepare("DELETE FROM tbl_sessions WHERE sid = '{1}'");
        $stmt->execute(array($id));
        //return ($stmt->rowCount() === 1) ? true : false;
        return TRUE;
    }

    public function gc($maxlifetime) {      
        $now = time();
        $req = self::$db->prepare("DELETE FROM tbl_sessions WHERE expire < '{1}' ");
        $req->execute(array($now));
        return TRUE;

    }

}

我按照这样初始化处理程序:

$handler = new Core_DB_SessionHandler($MyOptions, $MyPDO);
            if (PHP5) {
                if (!session_set_save_handler($handler, TRUE)) {
                    throw new Core_Exception('Erreur lors de l\'init des sessions !');
                }
            }

nb:在你的表结构中,不要使用自动增量ID

答案 1 :(得分:0)

好吧,我已经解决了我的问题,但是很难说这个问题是否真的是问题所在。

在read方法中,我更改了以下内容:

return $rel->fetch(PDO::FETCH_ASSOC)['session_data'];

$data $rel->fetch(PDO::FETCH_ASSOC)['session_data'];
return $data;

在此之后,会话正在将$session_data写入数据库而没有任何问题。这一切都很好,但是它并没有解释为什么它首先不起作用。我主要说这是因为在切换语句后,一切都继续工作。同样,我现在无法重现这个问题。所以我很难说这首先是问题所在。

希望这有助于某人。我一直无法找到有关它的更多信息,但它确实显示出来我肯定会在这里添加它。