<?php
/**
* a session handler similar to the default file handler
* But without locking
*
*/
class nonBlockingHandler extends stackSess {
private $fileName;
private $lastSessionId;
private $levels;
private $mode;
private $lockedOK;
private $overridePath;
private $newfile;
function close ()
{
$result_there=!$this->shStackNext;
$this->logit("nonBlockingHandler::close()");
if ($this->shStackNext) {
$result_there=$this->shStackNext->close();
}
return $result_there;
}
function destroy($session_id)
{
$result_here=false;
$result_there=!$this->shStackNext;
$this->logit("nonBlockingHandler::destroy('$session_id')");
$fname=$this->getFileName($session_id);
if (is_file($fname)) {
$result_here=unlink($fname);
}
if ($this->shStackNext) {
$result_there=$this->shStackNext->destroy($session_id);
}
return ($result_here && $result_there);
}
/**
* Note that unlike the default handler
* gc works on nested session directories
* as a result, gc may block for a long time
*/
function gc($maxlifetime)
{
$result_here=false;
$result_there=!$this->shStackNext;
$this->logit("nonBlockingHandler::gc($maxlifetime)");
$parts=ini_get('session.save_path');
$parts=explode(";",$parts);
$path=array_pop($parts);
$path=$this->overridePath ? $this->overridePath : $path;
$result_here=$this->gcDir($path, $maxlifetime,$this->levels);
if ($this->shStackNext) {
$result_there=$this->shStackNext->gc($maxlifetime);
}
return ($result_here && $result_there);
}
/**
* internal function used to recurse directories
*/
function gcDir($path, $maxlifetime,$levels)
{
$dh=opendir($path);
if (!$dh) {
return false;
}
$oldestAllowed=time()-$maxlifetime;
while (($file = readdir($dh)) !== false) {
if ('.'===$file) continue;
if ('..'===$file) continue;
$fullPath=$path . DIRECTORY_SEPARATOR . $file;
if (is_dir($fullPath) && $levels>=0) {
if (!$this->gcDir($fullPath,$maxlifetime,$levels-1)) {
closedir($dh);
return false;
}
} else if ($oldestAllowed>filemtime($fullPath)) {
if (!unlink($fullPath)) {
closedir($dh);
return false;
}
}
}
closedir($dh);
return true;
}
function open($save_path, $name)
{
$this->logit("nonBlockingHandler::open($save_path,$name)");
// wtf is this intended to do? read is done seperately?
// there's no point checking the directory is writeable/creating a file
// until we know what the session id is (in case of subdirs)
// although save_path is passed, it makes no sense when applied to multi
$this->overridePath=$save_path;
$result=true;
if ($this->shStackNext) {
$result=$this->shStackNext->open($save_path, $name);
}
return $result;
}
function read($session_id)
{
$this->logit("nonBlockingHandler::read($session_id)");
$this->lastSessionId=$session_id;
$fname=$this->getFileName($session_id);
$this->logit("* $fname " . ( file_exists($fname) ? " exists" : " does not exist"));
$filemode=file_exists($fname) ? 'r+' : 'w';
$this->lastAccess=0;
$this->newfile=true;
if (file_exists($fname)) {
$this->lastAccess=filemtime($fname);
$this->newfile=false;
}
$session_str='';
if ('r+'==$filemode) {
return file_get_contents($fname);
}
return '';
}
function write($session_id, $session_data)
{
$this->logit("nonBlockingHandler:: write($session_id, \$session_data)");
$result_here=false;
$result_there=!$this->shStackNext;
$fname=$this->getFileName($session_id);
clearstatcache();
$msg="acc=" . $this->lastAccess . " new=" . ($this->newfile ? 'true' : 'false');
$this->logit("DEBUG: $msg");
if ($this->lastAccess && !$this->newfile && filemtime($fname)>$this->lastAccess) {
trigger_error("nonBlockingHandler file contention", E_USER_ERROR);
}
$result_here=file_put_contents($fname, $session_data);
if ($this->shStackNext) {
$result_there=$this->shStackNext->write($session_id, $session_data);
}
return ($result_here && $result_there);
}
/**
* Note that create_sid was added in v5.5.0
* and is required for session data encryption
* NB, this is propogated but *only* one handler should
* create the sid
*/
function create_sid($newlyCreatedSid=false)
{
$this->newfile=true;
if (!$newlyCreatedSid) {
if (function_exists('openssl_random_pseudo_bytes')) {
$newlyCreatedSid=bin2hex(openssl_random_pseudo_bytes(16));
} else {
$newlyCreatedSid=md5(uniqid('',true));
}
}
if (is_callable(array($this->shStackNext,'create_sid'))) {
// send notification ONLY down stack
$this->shStackNext->create_sid($newlyCreatedSid);
}
return $newlyCreatedSid;
}
function getFileName($session_id)
{
if ($session_id==$this->lastSessionId && $this->fileName) {
return $this->fileName;
}
$parts=ini_get('session.save_path');
$parts=explode(";",$parts);
$path=array_pop($parts);
$path=$this->overridePath ? $this->overridePath : $path;
if (count($path)) {
$this->levels=array_shift($parts);
} else {
$this->levels=0;
}
if (count($path)) {
$this->mode=array_shift($parts);
} else {
$this->mode='0600';
}
$levels=$this->levels;
$offset=0;
while ($levels-$offset) {
$path.=DIRECTORY_SEPARATOR . substr($session_id, $offset,1);
$offset++;
}
$this->lastSessionId=$session_id;
$this->fileName=$path . DIRECTORY_SEPARATOR . 'sess_' . $session_id;
return $this->fileName;
}
function lastAccessed($lastAccess=false)
{
if (is_callable(array($this->shStackNext,'lastAccessed'))) {
$lastAccess=$this->shStackNext->lastAccessed($lastAccess);
}
if (!$lastAccess || $lastAccess>$this->lastAccess) {
return $this->lastAccess;
}
return $lastAccess;
}
}
|