add SessionHandler

In PHP 5.4 they added this class that encased the callback functions. The difficulty came with needing to fallback to the previously registered session handler.

Closes #792
Esse commit está contido em:
Paul Tarjan
2013-05-29 22:19:05 -07:00
commit de Sara Golemon
commit b9058d036e
12 arquivos alterados com 467 adições e 69 exclusões
+62 -34
Ver Arquivo
@@ -78,12 +78,7 @@ public:
int m_module_number;
int64_t m_cache_expire;
std::string m_ps_open;
std::string m_ps_close;
std::string m_ps_read;
std::string m_ps_write;
std::string m_ps_destroy;
std::string m_ps_gc;
Object m_ps_session_handler;
SessionSerializer *m_serializer;
@@ -717,21 +712,24 @@ public:
UserSessionModule() : SessionModule("user") {}
virtual bool open(const char *save_path, const char *session_name) {
return vm_call_user_func
(String(PS(ps_open)),
return vm_call_user_func(
CREATE_VECTOR2(PS(ps_session_handler),
String("open")),
CREATE_VECTOR2(String(save_path, CopyString),
String(session_name, CopyString)));
}
virtual bool close() {
return vm_call_user_func
(String(PS(ps_close)),
return vm_call_user_func(
CREATE_VECTOR2(PS(ps_session_handler),
String("close")),
Array::Create());
}
virtual bool read(const char *key, String &value) {
Variant ret = vm_call_user_func
(String(PS(ps_read)),
Variant ret = vm_call_user_func(
CREATE_VECTOR2(PS(ps_session_handler),
String("read")),
CREATE_VECTOR1(String(key, CopyString)));
if (ret.isString()) {
value = ret.toString();
@@ -741,20 +739,23 @@ public:
}
virtual bool write(const char *key, CStrRef value) {
return vm_call_user_func
(String(PS(ps_write)),
return vm_call_user_func(
CREATE_VECTOR2(PS(ps_session_handler),
String("write")),
CREATE_VECTOR2(String(key, CopyString), value));
}
virtual bool destroy(const char *key) {
return vm_call_user_func
(String(PS(ps_destroy)),
return vm_call_user_func(
CREATE_VECTOR2(PS(ps_session_handler),
String("destroy")),
CREATE_VECTOR1(String(key, CopyString)));
}
virtual bool gc(int maxlifetime, int *nrdels) {
return vm_call_user_func
(String(PS(ps_gc)),
return vm_call_user_func(
CREATE_VECTOR2(PS(ps_session_handler),
String("gc")),
CREATE_VECTOR1((int64_t)maxlifetime));
}
};
@@ -1360,27 +1361,17 @@ Variant f_session_module_name(CStrRef newname /* = null_string */) {
return oldname;
}
static bool check_handler(const char *name, std::string &member) {
if (!f_is_callable(name)) {
raise_warning("Argument '%s' is not a valid callback", name);
return false;
}
member = name;
return true;
}
bool f_hphp_session_set_save_handler(CObjRef sessionhandler,
bool register_shutdown /* = true */) {
bool f_session_set_save_handler(CStrRef open, CStrRef close, CStrRef read,
CStrRef write, CStrRef destroy, CStrRef gc) {
if (PS(session_status) != Session::None) {
return false;
}
if (!check_handler(open.data(), PS(ps_open))) return false;
if (!check_handler(close.data(), PS(ps_close))) return false;
if (!check_handler(read.data(), PS(ps_read))) return false;
if (!check_handler(write.data(), PS(ps_write))) return false;
if (!check_handler(destroy.data(), PS(ps_destroy))) return false;
if (!check_handler(gc.data(), PS(ps_gc))) return false;
PS(ps_session_handler) = sessionhandler;
if (register_shutdown) {
f_register_shutdown_function(1, String("session_write_close"));
}
IniSetting::Set("session.save_handler", "user");
return true;
@@ -1648,5 +1639,42 @@ bool f_session_is_registered(CStrRef varname) {
"Relying on this feature is highly discouraged.");
}
void c_SessionHandler::t___construct() { }
c_SessionHandler::c_SessionHandler(Class* cb) : ExtObjectData(cb) {
m_mod = PS(mod);
}
c_SessionHandler::~c_SessionHandler() { }
bool c_SessionHandler::t_open(CStrRef save_path, CStrRef session_id) {
return m_mod->open(save_path->data(), session_id->data());
}
bool c_SessionHandler::t_close() {
return m_mod->close();
}
String c_SessionHandler::t_read(CStrRef session_id) {
String value;
if (m_mod->read(PS(id).data(), value)) {
php_session_decode(value);
return value;
}
return uninit_null();
}
bool c_SessionHandler::t_write(CStrRef session_id, CStrRef session_data) {
return m_mod->write(session_id->data(), session_data->data());
}
bool c_SessionHandler::t_destroy(CStrRef session_id) {
return m_mod->destroy(session_id->data());
}
bool c_SessionHandler::t_gc(int maxlifetime) {
int nrdels = -1;
return m_mod->gc(maxlifetime, &nrdels);
}
///////////////////////////////////////////////////////////////////////////////
}
+28 -1
Ver Arquivo
@@ -29,7 +29,7 @@ void f_session_set_cookie_params(int64_t lifetime, CStrRef path = null_string, C
Array f_session_get_cookie_params();
String f_session_name(CStrRef newname = null_string);
Variant f_session_module_name(CStrRef newname = null_string);
bool f_session_set_save_handler(CStrRef open, CStrRef close, CStrRef read, CStrRef write, CStrRef destroy, CStrRef gc);
bool f_session_set_save_handler(CObjRef sessionhandler, bool register_shutdown = true);
String f_session_save_path(CStrRef newname = null_string);
String f_session_id(CStrRef newid = null_string);
bool f_session_regenerate_id(bool delete_old_session = false);
@@ -46,6 +46,33 @@ bool f_session_register(int _argc, CVarRef var_names, CArrRef _argv = null_array
bool f_session_unregister(CStrRef varname);
bool f_session_is_registered(CStrRef varname);
///////////////////////////////////////////////////////////////////////////////
// class SessionHandler
class SessionModule;
FORWARD_DECLARE_CLASS_BUILTIN(SessionHandler);
class c_SessionHandler : public ExtObjectData {
public:
DECLARE_CLASS(SessionHandler, SessionHandler, ObjectData)
// need to implement
public: c_SessionHandler(Class* cls = c_SessionHandler::s_cls);
public: ~c_SessionHandler();
public: void t___construct();
public: bool t_open(CStrRef save_path, CStrRef session_id);
public: bool t_close();
public: String t_read(CStrRef session_id);
public: bool t_write(CStrRef session_id, CStrRef session_data);
public: bool t_destroy(CStrRef session_id);
public: bool t_gc(int maxlifetime);
// implemented by HPHP
public: c_SessionHandler *create();
private: SessionModule* m_mod;
};
///////////////////////////////////////////////////////////////////////////////
}
+107 -34
Ver Arquivo
@@ -97,45 +97,19 @@
]
},
{
"name": "session_set_save_handler",
"desc": "session_set_save_handler() sets the user-level session storage functions which are used for storing and retrieving data associated with a session. This is most useful when a storage method other than those supplied by PHP sessions is preferred. i.e. Storing the session data in a local database.",
"flags": [
"HasDocComment"
],
"name": "hphp_session_set_save_handler",
"return": {
"type": "Boolean",
"desc": "Returns TRUE on success or FALSE on failure."
"type": "Boolean"
},
"args": [
{
"name": "open",
"type": "String",
"desc": "Open function, this works like a constructor in classes and is executed when the session is being opened. The open function expects two parameters, where the first is the save path and the second is the session name."
"name": "sessionhandler",
"type": "Object"
},
{
"name": "close",
"type": "String",
"desc": "Close function, this works like a destructor in classes and is executed when the session operation is done."
},
{
"name": "read",
"type": "String",
"desc": "Read function must return string value always to make save handler work as expected. Return empty string if there is no data to read. Return values from other handlers are converted to boolean expression. TRUE for success, FALSE for failure."
},
{
"name": "write",
"type": "String",
"desc": "Write function that is called when session data is to be saved. This function expects two parameters: an identifier and the data associated with it.\n\nThe \"write\" handler is not executed until after the output stream is closed. Thus, output from debugging statements in the \"write\" handler will never be seen in the browser. If debugging output is necessary, it is suggested that the debug output be written to a file instead."
},
{
"name": "destroy",
"type": "String",
"desc": "The destroy handler, this is executed when a session is destroyed with session_destroy() and takes the session id as its only parameter."
},
{
"name": "gc",
"type": "String",
"desc": "The garbage collector, this is executed when the session garbage collector is executed and takes the max session lifetime as its only parameter."
"name": "register_shutdown",
"type": "Boolean",
"value": "true"
}
]
},
@@ -384,5 +358,104 @@
}
],
"classes": [
{
"name": "SessionHandler",
"desc": "",
"ifaces": [
"SessionHandlerInterface"
],
"flags": [
"HasDocComment"
],
"funcs": [
{
"name": "__construct",
"flags": [
"HasDocComment"
],
"return": {
"type": null
},
"args": [
]
},
{
"name": "open",
"return": {
"type": "Boolean"
},
"args": [
{
"name": "save_path",
"type": "String"
},
{
"name": "session_id",
"type": "String"
}
]
},
{
"name": "close",
"return": {
"type": "Boolean"
},
"args": [
]
},
{
"name": "read",
"return": {
"type": "String"
},
"args": [
{
"name": "session_id",
"type": "String"
}
]
},
{
"name": "write",
"return": {
"type": "Boolean"
},
"args": [
{
"name": "session_id",
"type": "String"
},
{
"name": "session_data",
"type": "String"
}
]
},
{
"name": "destroy",
"return": {
"type": "Boolean"
},
"args": [
{
"name": "session_id",
"type": "String"
}
]
},
{
"name": "gc",
"return": {
"type": "Boolean"
},
"args": [
{
"name": "maxlifetime",
"type": "Int32"
}
]
}
]
}
]
}
}
+3
Ver Arquivo
@@ -41,6 +41,9 @@ hphp/system/php/filter/filter_input.php
hphp/system/php/filter/filter_var_array.php
hphp/system/php/filter/filter_input_array.php
hphp/system/php/sessions/SessionHandlerInterface.php
hphp/system/php/sessions/session_set_save_handler.php
# If you have no inheritance relationship, go here in alphabetical order
hphp/system/php/DebuggerCommand.php
hphp/system/php/XhprofFrame.php
@@ -0,0 +1,145 @@
<?php
// This doc comment block generated by idl/sysdoc.php
/**
* ( excerpt from
* http://php.net/manual/en/class.sessionhandlerinterface.php )
*
* SessionHandlerInterface is an interface which defines a prototype for
* creating a custom session handler. In order to pass a custom session
* handler to session_set_save_handler() using its OOP invocation, the
* class must implement this interface.
*
* Please note the callback methods of this class are designed to be
* called internally by PHP and are not meant to be called from user-space
* code.
*
*/
interface SessionHandlerInterface {
// This doc comment block generated by idl/sysdoc.php
/**
* ( excerpt from
* http://php.net/manual/en/sessionhandlerinterface.close.php )
*
* Closes the current session. This function is automatically executed
* when closing the session, or explicitly via session_write_close().
*
* @return mixed The return value (usually TRUE on success, FALSE on
* failure). Note this value is returned internally to
* PHP for processing.
*/
public function close();
// This doc comment block generated by idl/sysdoc.php
/**
* ( excerpt from
* http://php.net/manual/en/sessionhandlerinterface.destroy.php )
*
* Destroys a session. Called by session_regenerate_id() (with $destroy =
* TRUE), session_destroy() and when session_decode() fails.
*
* @session_id mixed The session ID being destroyed.
*
* @return mixed The return value (usually TRUE on success, FALSE on
* failure). Note this value is returned internally to
* PHP for processing.
*/
public function destroy($session_id);
// This doc comment block generated by idl/sysdoc.php
/**
* ( excerpt from http://php.net/manual/en/sessionhandlerinterface.gc.php )
*
* Cleans up expired sessions. Called by session_start(), based on
* session.gc_divisor, session.gc_probability and session.gc_lifetime
* settings.
*
* @maxlifetime
* mixed Sessions that have not updated for the last
* maxlifetime seconds will be removed.
*
* @return mixed The return value (usually TRUE on success, FALSE on
* failure). Note this value is returned internally to
* PHP for processing.
*/
public function gc($maxlifetime);
// This doc comment block generated by idl/sysdoc.php
/**
* ( excerpt from http://php.net/manual/en/sessionhandlerinterface.open.php
* )
*
* Re-initialize existing session, or creates a new one. Called when a
* session starts or when session_start() is invoked.
*
* @save_path mixed The path where to store/retrieve the session.
* @name mixed The session name.
*
* @return mixed The return value (usually TRUE on success, FALSE on
* failure). Note this value is returned internally to
* PHP for processing.
*/
public function open($save_path, $name);
// This doc comment block generated by idl/sysdoc.php
/**
* ( excerpt from http://php.net/manual/en/sessionhandlerinterface.read.php
* )
*
* Reads the session data from the session storage, and returns the
* results. Called right after the session starts or when session_start()
* is called. Please note that before this method is called
* SessionHandlerInterface::open() is invoked.
*
* This method is called by PHP itself when the session is started. This
* method should retrieve the session data from storage by the session ID
* provided. The returned by this method must be in the same
* serialized format as when originally passed to the
* SessionHandlerInterface::write() If the record was not found, return an
* empty string.
*
* The data returned by this method will be decoded internally by PHP
* using the unserialization method specified in session.serialize_handler.
* The resultig data will be used to populate the $_SESSION superglobal.
*
* Note that the serialization scheme is not the same as unserialize() and
* can be accessed by session_decode().
*
* @session_id mixed The session id.
*
* @return mixed Returns an encoded of the read data. If
* nothing was read, it must return an empty string.
* Note this value is returned internally to PHP for
* processing.
*/
public function read($session_id);
// This doc comment block generated by idl/sysdoc.php
/**
* ( excerpt from
* http://php.net/manual/en/sessionhandlerinterface.write.php )
*
* Writes the session data to the session storage. Called by
* session_write_close(), when session_register_shutdown() fails, or during
* a normal shutdown. Note: SessionHandlerInterface::close() is called
* immediately after this function.
*
* PHP will call this method when the session is ready to be saved and
* closed. It encodes the session data from the $_SESSION superglobal to a
* serialized and passes this along with the session ID to this
* method for storage. The serialization method used is specified in the
* session.serialize_handler setting.
*
* Note this method is normally called by PHP after the output buffers
* have been closed unless explicitly called by session_write_close()
*
* @session_id mixed The session id.
* @session_data
* mixed The encoded session data. This data is the result of
* the PHP internally encoding the $_SESSION
* superglobal to a serialized and passing it as
* this parameter. Please note sessions use an
* alternative serialization method.
*
* @return mixed The return value (usually TRUE on success, FALSE on
* failure). Note this value is returned internally to
* PHP for processing.
*/
public function write($session_id, $session_data);
}
@@ -0,0 +1,72 @@
<?php
class _SessionForwardingHandler implements SessionHandlerInterface {
private $open;
private $close;
private $read;
private $write;
private $destory;
private $gc;
public function __construct($open, $close, $read, $write, $destroy, $gc) {
try {
$this->open = $this->validate($open, 1);
$this->close = $this->validate($close, 2);
$this->read = $this->validate($read, 3);
$this->write = $this->validate($write, 4);
$this->destroy = $this->validate($destroy, 5);
$this->gc = $this->validate($gc, 6);
} catch (Exception $e) {
trigger_error($e->getMessage(), E_USER_WARNING);
return false;
}
}
public function open($save_path, $session_id) {
if ($this->open) {
return call_user_func($this->open, $save_path, $session_id);
}
}
public function close() {
if ($this->close) {
return call_user_func($this->close);
}
}
public function read($session_id) {
if ($this->read) {
return call_user_func($this->read, $session_id);
}
}
public function write($session_id, $session_data) {
if ($this->write) {
return call_user_func($this->write, $session_id, $session_data);
}
}
public function destroy($session_id) {
if ($this->destroy) {
return call_user_func($this->destroy, $session_id);
}
}
public function gc($maxlifetime) {
if ($this->gc) {
return call_user_func($this->gc, $maxlifetime);
}
}
private function validate($func, $num) {
if (!is_callable($func)) {
throw new Exception("Argument $num is not a valid callback");
}
return $func;
}
}
function session_set_save_handler(
$open,
$close = null, $read = null, $write = null, $destroy = null, $gc = null) {
if ($open instanceof SessionHandlerInterface) {
return hphp_session_set_save_handler($open, $close);
}
return hphp_session_set_save_handler(
new _SessionForwardingHandler($open, $close, $read, $write, $destroy, $gc)
);
}
@@ -0,0 +1,29 @@
<?php
class EncryptedSessionHandler extends SessionHandler {
private $key;
public function __construct($key) {
$this->key = $key;
}
public function read($id) {
$data = parent::read($id);
var_dump($data);
return mcrypt_decrypt(MCRYPT_3DES, $this->key, $data, MCRYPT_MODE_ECB);
}
public function write($id, $data) {
$data = mcrypt_encrypt(MCRYPT_3DES, $this->key, $data, MCRYPT_MODE_ECB);
var_dump($data);
return parent::write($id, $data);
}
}
ini_set('session.save_handler', 'files');
$handler = new EncryptedSessionHandler('mykey');
session_set_save_handler($handler, true);
session_start();
$_SESSION['a'] = 'A';
var_dump($_SESSION['a']);
@@ -0,0 +1,3 @@
string(0) ""
string(1) "A"
string(16) "¤zBN÷¼£§¤¼T8¬k·"
@@ -0,0 +1,14 @@
<?php
$session_handler = new SessionHandler();
session_set_save_handler(
array($session_handler, 'open'),
array($session_handler, 'close'),
array($session_handler, 'read'),
array($session_handler, 'write'),
array($session_handler, 'destroy'),
array($session_handler, 'gc')
);
$_SESSION['a'] = 'A';
var_dump($_SESSION);
@@ -0,0 +1,4 @@
array(1) {
["a"]=>
string(1) "A"
}