Back to index

d-push  2.0
Public Member Functions | Public Attributes | Private Member Functions | Private Attributes
FileStateMachine Class Reference
Inheritance diagram for FileStateMachine:
Inheritance graph
[legend]
Collaboration diagram for FileStateMachine:
Collaboration graph
[legend]

List of all members.

Public Member Functions

 FileStateMachine ()
 Constructor.
 GetStateHash ($devid, $type, $key=false, $counter=false)
 Gets a hash value indicating the latest dataset of the named state with a specified key and counter.
 GetState ($devid, $type, $key=false, $counter=false, $cleanstates=true)
 Gets a state for a specified key and counter.
 SetState ($state, $devid, $type, $key=false, $counter=false)
 Writes ta state to for a key and counter.
 CleanStates ($devid, $type, $key, $counter=false)
 Cleans up all older states If called with a $counter, all states previous state counter can be removed If called without $counter, all keys (independently from the counter) can be removed.
 LinkUserDevice ($username, $devid)
 Links a user to a device.
 UnLinkUserDevice ($username, $devid)
 Unlinks a device from a user.
 GetAllDevices ($username=false)
 Returns an array with all device ids for a user.

Public Attributes

const DEFTYPE = ""
const DEVICEDATA = "devicedata"
const FOLDERDATA = "fd"
const FAILSAVE = "fs"
const HIERARCHY = "hc"
const BACKENDSTORAGE = "bs"

Private Member Functions

 getFullFilePath ($devid, $type, $key=false, $counter=false, $doNotCreateDirs=false)
 
Private FileStateMachine stuff
 getDirectoryForDevice ($devid, $doNotCreateDirs=false)
 Checks if the configured path exists and if a subfolder structure is available A two level deep subdirectory structure is build to save the states.

Private Attributes

 $userfilename

Detailed Description

Definition at line 48 of file filestatemachine.php.


Member Function Documentation

FileStateMachine::CleanStates ( devid,
type,
key,
counter = false 
)

Cleans up all older states If called with a $counter, all states previous state counter can be removed If called without $counter, all keys (independently from the counter) can be removed.

Parameters:
string$devidthe device id
string$typethe state type
string$key
string$counter(opt)

public

Returns:
Exceptions:
StateInvalidException

Implements IStateMachine.

Definition at line 171 of file filestatemachine.php.

                                                                       {
        $matching_files = glob($this->getFullFilePath($devid, $type, $key). "*", GLOB_NOSORT);
        if (is_array($matching_files)) {
            foreach($matching_files as $state) {
                $file = false;
                if($counter !== false && preg_match('/([0-9]+)$/', $state, $matches)) {
                    if($matches[1] < $counter) {
                        $candidate = $this->getFullFilePath($devid, $type, $key, (int)$matches[1]);

                        if ($candidate == $state)
                            $file = $candidate;
                    }
                }
                else if ($counter === false)
                    $file =  $this->getFullFilePath($devid, $type, $key);

                if ($file !== false) {
                    ZLog::Write(LOGLEVEL_DEBUG, sprintf("FileStateMachine->CleanStates(): Deleting file: '%s'", $file));
                    unlink ($file);
                }
            }
        }
    }

Here is the call graph for this function:

Here is the caller graph for this function:

Constructor.

Performs some basic checks and initilizes the state directory

public

Exceptions:
FatalMisconfigurationException

Definition at line 59 of file filestatemachine.php.

                                       {
        if (!defined('STATE_DIR'))
            throw new FatalMisconfigurationException("No configuration for the state directory available.");

        if (substr(STATE_DIR, -1,1) != "/")
            throw new FatalMisconfigurationException("The configured state directory should terminate with a '/'");

        if (!file_exists(STATE_DIR))
            throw new FatalMisconfigurationException("The configured state directory does not exist or can not be accessed: ". STATE_DIR);
        // checks if the directory exists and tries to create the necessary subfolders if they do not exist
        $this->getDirectoryForDevice(Request::GetDeviceID());
        $this->userfilename = STATE_DIR . 'users';

        if (!touch($this->userfilename))
            throw new FatalMisconfigurationException("Not possible to write to the configured state directory.");
    }

Here is the call graph for this function:

FileStateMachine::GetAllDevices ( username = false)

Returns an array with all device ids for a user.

If no user is set, all device ids should be returned

Parameters:
string$username(opt)

public

Returns:
array

Implements IStateMachine.

Definition at line 298 of file filestatemachine.php.

                                                     {
        $out = array();
        if ($username === false) {
            foreach (glob(STATE_DIR. "/*/*/*-".IStateMachine::DEVICEDATA, GLOB_NOSORT) as $devdata)
                if (preg_match('/\/([A-Za-z0-9]+)-'. IStateMachine::DEVICEDATA. '$/', $devdata, $matches))
                    $out[] = $matches[1];
            return $out;
        }
        else {
            $filecontents = file_get_contents($this->userfilename);
            if ($filecontents)
                $users = unserialize($filecontents);
            else
                $users = array();

            // get device list for the user
            if (isset($users[$username]))
                return array_keys($users[$username]);
            else
                return array();
        }
    }
FileStateMachine::getDirectoryForDevice ( devid,
doNotCreateDirs = false 
) [private]

Checks if the configured path exists and if a subfolder structure is available A two level deep subdirectory structure is build to save the states.

The subdirectories where to save, are determined with device id

Parameters:
string$devidthe device id
boolen$doNotCreateDirs(opt) by default false - indicates if the subdirs should be created

private

Returns:
string/boolean returns the full directory of false if the dirs can not be created
Exceptions:
FatalMisconfigurationExceptionwhen configured directory is not writeable

Definition at line 361 of file filestatemachine.php.

                                                                             {
        $firstLevel = substr(strtolower($devid), -1, 1);
        $secondLevel = substr(strtolower($devid), -2, 1);

        $dir = STATE_DIR . $firstLevel . "/" . $secondLevel;
        if (is_dir($dir))
            return $dir;

        if ($doNotCreateDirs === false) {
            // try to create the subdirectory structure necessary
            $fldir = STATE_DIR . $firstLevel;
            if (!is_dir($fldir)) {
                $dirOK = mkdir($fldir);
                if (!$dirOK)
                    throw new FatalMisconfigurationException("FileStateMachine->getDirectoryForDevice(): Not possible to create state sub-directory: ". $fldir);
            }

            if (!is_dir($dir)) {
                $dirOK = mkdir($dir);
                if (!$dirOK)
                    throw new FatalMisconfigurationException("FileStateMachine->getDirectoryForDevice(): Not possible to create state sub-directory: ". $dir);
            }
            else
                return $dir;
        }
        return false;
    }

Here is the caller graph for this function:

FileStateMachine::getFullFilePath ( devid,
type,
key = false,
counter = false,
doNotCreateDirs = false 
) [private]


Private FileStateMachine stuff

Returns the full path incl. filename for a key (generally uuid) and a counter

Parameters:
string$devidthe device id
string$typethe state type
string$key(opt)
string$counter(opt) default false
boolean$doNotCreateDirs(opt) indicates if missing subdirectories should be created, default false

private

Returns:
string
Exceptions:
StateInvalidException

Definition at line 339 of file filestatemachine.php.

                                                                                                              {
        $testkey = $devid . (($key !== false)? "-". $key : "") . (($type !== "")? "-". $type : "");
        if (preg_match('/^[a-zA-Z0-9-]+$/', $testkey, $matches) || ($type == "" && $key === false))
            $internkey = $testkey . (($counter && is_int($counter))?"-".$counter:"");
        else
            throw new StateInvalidException("FileStateMachine->getFullFilePath(): Invalid state deviceid, type, key or in any combination");

        return $this->getDirectoryForDevice($devid, $doNotCreateDirs) ."/". $internkey;
    }

Here is the call graph for this function:

Here is the caller graph for this function:

FileStateMachine::GetState ( devid,
type,
key = false,
counter = false,
cleanstates = true 
)

Gets a state for a specified key and counter.

This method sould call IStateMachine->CleanStates() to remove older states (same key, previous counters)

Parameters:
string$devidthe device id
string$typethe state type
string$key(opt)
string$counter(opt)
string$cleanstates(opt)

public

Returns:
mixed
Exceptions:
StateNotFoundException,StateInvalidException

Implements IStateMachine.

Definition at line 116 of file filestatemachine.php.

                                                                                                 {
        if ($counter && $cleanstates)
            $this->CleanStates($devid, $type, $key, $counter);

        // Read current sync state
        $filename = $this->getFullFilePath($devid, $type, $key, $counter);

        ZLog::Write(LOGLEVEL_DEBUG, sprintf("FileStateMachine->GetState() on file: '%s'", $filename));

        if(file_exists($filename)) {
            return unserialize(file_get_contents($filename));
        }
        // throw an exception on all other states, but not FAILSAVE as it's most of the times not there by default
        else if ($type !== IStateMachine::FAILSAVE)
            throw new StateNotFoundException(sprintf("FileStateMachine->GetState(): Could not locate state '%s'",$filename));
    }

Here is the call graph for this function:

FileStateMachine::GetStateHash ( devid,
type,
key = false,
counter = false 
)

Gets a hash value indicating the latest dataset of the named state with a specified key and counter.

If the state is changed between two calls of this method the returned hash should be different

Parameters:
string$devidthe device id
string$typethe state type
string$key(opt)
string$counter(opt)

public

Returns:
string
Exceptions:
StateNotFoundException,StateInvalidException

Implements IStateMachine.

Definition at line 91 of file filestatemachine.php.

                                                                                {
        $filename = $this->getFullFilePath($devid, $type, $key, $counter);

        // the filemodification time is enough to track changes
        if(file_exists($filename))
            return filemtime($filename);
        else
            throw new StateNotFoundException(sprintf("FileStateMachine->GetStateHash(): Could not locate state '%s'",$filename));
    }

Here is the call graph for this function:

FileStateMachine::LinkUserDevice ( username,
devid 
)

Links a user to a device.

Parameters:
string$username
string$devidpublic
Returns:
array

Implements IStateMachine.

Definition at line 204 of file filestatemachine.php.

                                                      {
        include_once("simplemutex.php");
        $mutex = new SimpleMutex();

        // exclusive block
        if ($mutex->Block()) {
            $filecontents = @file_get_contents($this->userfilename);

            if ($filecontents)
                $users = unserialize($filecontents);
            else
                $users = array();

            $changed = false;

            // add user/device to the list
            if (!isset($users[$username])) {
                $users[$username] = array();
                $changed = true;
            }
            if (!isset($users[$username][$devid])) {
                $users[$username][$devid] = 1;
                $changed = true;
            }

            if ($changed) {
                $bytes = file_put_contents($this->userfilename, serialize($users));
                ZLog::Write(LOGLEVEL_DEBUG, sprintf("FileStateMachine->LinkUserDevice(): wrote %d bytes to users file", $bytes));
            }
            else
                ZLog::Write(LOGLEVEL_DEBUG, "FileStateMachine->LinkUserDevice(): nothing changed");

            $mutex->Release();
        }
    }

Here is the call graph for this function:

FileStateMachine::SetState ( state,
devid,
type,
key = false,
counter = false 
)

Writes ta state to for a key and counter.

Parameters:
mixed$state
string$devidthe device id
string$typethe state type
string$key(opt)
int$counter(opt)

public

Returns:
boolean
Exceptions:
StateInvalidException

Implements IStateMachine.

Definition at line 146 of file filestatemachine.php.

                                                                                    {
        $state = serialize($state);

        $filename = $this->getFullFilePath($devid, $type, $key, $counter);
        if (($bytes = file_put_contents($filename, $state)) === false)
            throw new FatalMisconfigurationException(sprintf("FileStateMachine->SetState(): Could not write state '%s'",$filename));

        ZLog::Write(LOGLEVEL_DEBUG, sprintf("FileStateMachine->SetState() written %d bytes on file: '%s'", $bytes, $filename));
        return $bytes;
    }

Here is the call graph for this function:

FileStateMachine::UnLinkUserDevice ( username,
devid 
)

Unlinks a device from a user.

Parameters:
string$username
string$devidpublic
Returns:
array

Implements IStateMachine.

Definition at line 249 of file filestatemachine.php.

                                                        {
        include_once("simplemutex.php");
        $mutex = new SimpleMutex();

        // exclusive block
        if ($mutex->Block()) {
            $filecontents = @file_get_contents($this->userfilename);

            if ($filecontents)
                $users = unserialize($filecontents);
            else
                $users = array();

            $changed = false;

            // is this user listed at all?
            if (isset($users[$username])) {
                if (isset($users[$username][$devid])) {
                    unset($users[$username][$devid]);
                    $changed = true;
                }

                // if there is no device left, remove the user
                if (empty($users[$username])) {
                    unset($users[$username]);
                    $changed = true;
                }
            }

            if ($changed) {
                $bytes = file_put_contents($this->userfilename, serialize($users));
                ZLog::Write(LOGLEVEL_DEBUG, sprintf("FileStateMachine->UnLinkUserDevice(): wrote %d bytes to users file", $bytes));
            }
            else
                ZLog::Write(LOGLEVEL_DEBUG, "FileStateMachine->UnLinkUserDevice(): nothing changed");

            $mutex->Release();
        }
    }

Here is the call graph for this function:


Member Data Documentation

Definition at line 49 of file filestatemachine.php.

const IStateMachine::BACKENDSTORAGE = "bs" [inherited]

Definition at line 62 of file istatemachine.php.

const IStateMachine::DEFTYPE = "" [inherited]

Definition at line 57 of file istatemachine.php.

const IStateMachine::DEVICEDATA = "devicedata" [inherited]

Definition at line 58 of file istatemachine.php.

const IStateMachine::FAILSAVE = "fs" [inherited]

Definition at line 60 of file istatemachine.php.

const IStateMachine::FOLDERDATA = "fd" [inherited]

Definition at line 59 of file istatemachine.php.

const IStateMachine::HIERARCHY = "hc" [inherited]

Definition at line 61 of file istatemachine.php.


The documentation for this class was generated from the following file: