PHP Classes

File: handlers/compat.inc.php

Recommend this page to a friend!
  Classes of Colin McKinnon   Stackable PHP Session Handler   handlers/compat.inc.php   Download  
File: handlers/compat.inc.php
Role: Class source
Content type: text/plain
Description: Compatible Session Handler
Class: Stackable PHP Session Handler
Store session data with multiple session handlers
Author: By
Last change:
Date: 8 years ago
Size: 7,154 bytes
 

Contents

Class file image Download
<?php
/**
 * a session handler intended as a native PHP replacement
 * for the default file handler.
 *
 * NB this is not *completely* compatible
 * - gc works on multi level directories
 * - the default umask rather the permissions in session.save_path are applied to new files
 */
class compatSessionHandler extends stackSess {
    private
$fileHandle;
    private
$fileName;
    private
$lastSessionId;
    private
$levels;
    private
$mode;
    private
$lockedOK;
    private
$overridePath;

    function
close ()
    {
       
$result_here=false;
       
$result_there=!$this->shStackNext;
       
$this->logit("compatSessionHandler::close()");
       
$result_here=fclose($this->fileHandle);
        if (
$this->shStackNext) {
           
$result_there=$this->shStackNext->close();
        }
        return (
$result_here && $result_there);
    }
    function
destroy($session_id)
     {
       
$result_here=false;
       
$result_there=!$this->shStackNext;
       
$this->logit("compatSessionHandler::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("compatSessionHandler::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("compatSessionHandler::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("compatSessionHandler::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=file_exists($fname) ? filemtime($fname) : 0;
       
$session_str='';
       
       
// NB there's no calls passed down the stack - what if we had different
        // session strings?
       
if ($this->fileHandle=fopen($fname,$filemode)) {
           
$this->logit("openmode=$filemode handle=" . $this->fileHandle);
            if (
flock($this->fileHandle, LOCK_EX)) {
               
$this->lockedOK=true;
                while (
'r+'==$filemode && !feof($this->fileHandle)) {
                   
$session_str.=fread($this->fileHandle, 8096);
                }
                return
$session_str;
            }
           
$this->logit("Failed to lock session file in compatSessionHandler::read()");
        } else {
           
$this->logit("Failed to open session file in compatSessionHandler::read()");
        }
        return
'';
    }
    function
write($session_id, $session_data)
    {
       
$this->logit("compatSessionHandler:: write($session_id, \$session_data)");
       
$result_here=false;
       
$result_there=!$this->shStackNext;

       
// if session id is regenerated we are still pointing to the old file!
       
if ($session_id!=$this->lastSessionId) {
           
$this->logit("* in compatSessionHandler::write writing diffrent session than opened");
           
$this->close();
           
$this->fileName=false; // clear the cached filename
           
$this->read($session_id); // initialize file and filehandle
       
} else {
           
$this->logit("* in compatSessionHandler::write id unchanged");
        }
       
// although we don't read 2 session strings, we might want to write it twice
        // e.g. for replication
       
if ($this->lockedOK) {
           
ftruncate($this->fileHandle,0);
           
rewind($this->fileHandle);
           
$result_here=fwrite($this->fileHandle, $session_data) ? true : false;
           
$this->logit("* write result = $result_here");
        } else {
           
$this->logit("compatSessionHandler:: write failed");
        }
        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)
    {
        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
addNext($slavedHandler)
    {
       
trigger_error("The compatiblity session handler must be at the bottom of the stack", E_USER_ERROR);
        return
false;
    }
    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;
    }
}