| 
<?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;
 }
 }
 
 |