PHP Classes

File: handlers/nonBlocking.inc.php

Recommend this page to a friend!
  Classes of Colin McKinnon   Stackable PHP Session Handler   handlers/nonBlocking.inc.php   Download  
File: handlers/nonBlocking.inc.php
Role: Class source
Content type: text/plain
Description: Using non-blocking file IO
Class: Stackable PHP Session Handler
Store session data with multiple session handlers
Author: By
Last change:
Date: 8 years ago
Size: 5,988 bytes
 

Contents

Class file image Download
<?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;
        }
}