<?php
/**
 * A class for handling file-based session storage.
 */
class session_handler_file implements session_handler
{
    /**
     * @var string The session file directory.
     */
    protected $directory;
    
    ////////////////////////////////////////////////////////////////////////////
    // Construct and destruct functions.

    ////////////////////////////////////////////////////////////////////////////
    // Overload functions.

    ////////////////////////////////////////////////////////////////////////////
    // Interface functions.

    /**
     * Opens a session.
     * 
     * @param string $directory The session file directory.
     * @param string $name The session name.
     * @return boolean True if successful, else false.
     */
    public function open($directory, $name)
    {
        $this->directory = rtrim($directory, ' \\/') . '/';
        return true;
    }

    /**
     * Closes a session.
     * 
     * @return boolean True if successful, else false.
     */
    public function close()
    {
        return true;
    }

    /**
     * Reads from a session storage.
     * 
     * @param integer $id The session id.
     * @throws logicexception If the session id is not valid.
     * @throws unexpectedvalueexception If the session file could not be read.
     * @return array The session data.
     */
    public function read($id)
    {
        if (!$this->is_id_valid($id))
        {
            throw new logicexception('Session id is not valid.');
        }
        
        $file = $this->directory . 'sess_' . $id;
        $data = '';
        
        if (file_exists($file))
        {
            $data = file_get_contents($file);
            
            if ($data === false)
            {
                throw new unexpectedvalueexception('Session file ' . var_export($file, true) . ' could not be read.');
            }
        }
        
        return $data;
    }

    /**
     * Writes to a session storage.
     * 
     * @param integer $id The session id.
     * @param array $data The session data.
     * @return boolean True if successful, else false.
     */
    public function write($id, $data)
    {
        if (!$this->is_id_valid($id))
        {
            return false;
        }
        
        $file = $this->directory . 'sess_' . $id;
        
        if (file_put_contents($file, $data, 2) === false)
        {
            return false;
        }
        
        return true;
    }

    /**
     * Destroys a session.
     * 
     * @param integer $id The session id.
     * @throws logicexception If the session id is not valid.
     * @return boolean True if successful, else false.
     */
    public function destroy($id)
    {
        if (!$this->is_id_valid($id))
        {
            throw new logicexception('Session id is not valid.');
        }
        
        $file = $this->directory . 'sess_' . $id;
        
        if (is_file($file))
        {
            unlink($file);
        }
        
        return true;
    }

    /**
     * Clears expired sessions.
     * 
     * @param integer $life The expiration limit.
     * @return boolean True if successful, else false.
     */
    public function clear($life)
    {
        $time = time();
        
        foreach ((array) glob($this->directory . 'sess_*') as $f)
        {
            if ((filemtime($f) + $life < $time))
            {
                unlink($f);
            }
        }
        
        return true;
    }

    ////////////////////////////////////////////////////////////////////////////
    // Internal functions.

    /**
     * Checks if a session is valid or not.
     * 
     * @param string $id The session id.
     * @return boolean True if valid, else false.
     */
    protected function is_id_valid($id)
    {
        return (boolean) preg_match('~^[a-z0-9]+$~i', $id);
    }
}
