<?php
/**
 * A class for handling filesystem operations.
 */
class folder
{
    ////////////////////////////////////////////////////////////////////////////
    // Construct and destruct functions.

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

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

    /**
     * Creates a folder.
     * 
     * @param string $target The folder name.
     * @param integer (optional) $mode The permissions.
     * @throws logicexception If the folder already exist.
     * @throws unexpectedvalueexception If the folder could not be created.
     */
    public static function create($target, $mode = 0755)
    {
        if (file_exists($target))
        {
            throw new logicexception('Folder ' . var_export($target, true) . ' already exist.');
        }
        
        if (!mkdir($target))
        {
            throw new unexpectedvalueexception('Folder ' . var_export($target, true) . ' could not be created.');
        }
        
        self::mode($target, $mode);
    }

    /**
     * Deletes a file or folder.
     * 
     * @param string $source The file or folder name.
     * @throws unexpectedvalueexception If the folder could not be opened.
     * @throws unexpectedvalueexception If the file or folder could not be
     * deleted.
     */
    public static function delete($source)
    {
        if (is_dir($source))
        {
            $folder = dir($source);
            
            if (!$folder)
            {
                throw new unexpectedvalueexception('Folder could not be opened.');
            }
            
            while (($file = $folder->read()) !== false)
            {
                if ($file === '.' || $file === '..')
                {
                    continue;
                }
                
                self::delete($source . '/' . $file);
            }
            
            $folder->close();
            
            if (!rmdir($source))
            {
                throw new unexpectedvalueexception('Folder ' . var_export($source, true) . ' could not be deleted.');
            }
        }
        else
        {
            if (!unlink($source))
            {
                throw new unexpectedvalueexception('File ' . var_export($source, true) . ' could not be deleted.');
            }
        }
    }

    /**
     * Renames a file or folder.
     * 
     * @param string $source The current name.
     * @param string $target The new name.
     * @throws logicexception If the source file or folder is missing.
     * @throws logicexception If the target file or folder already exist.
     * @throws unexpectedvalueexception If the file or folder could not be
     * renamed.
     */
    public static function rename($source, $target)
    {
        if (!file_exists($source))
        {
            throw new logicexception('File or folder ' . var_export($source, true) . ' is missing.');
        }
        
        if (file_exists($target))
        {
            throw new logicexception('File or folder ' . var_export($target, true) . ' already exist.');
        }
        
        if (!rename($source, $target))
        {
            throw new unexpectedvalueexception('File or folder ' . var_export($source, true) . ' could not be renamed to ' . var_export($target, true) . '.');
        }
    }

    /**
     * Copies a file or folder.
     * 
     * @param string $source The file or folder name.
     * @param string $target The copy name.
     * @throws logicexception If the source file or folder is missing.
     * @throws logicexception If the target file or folder already exist.
     * @throws unexpectedvalueexception If the folder could not be opened.
     * @throws unexpectedvalueexception If the file or folder could not be
     * copied.
     */
    public static function copy($source, $target)
    {
        if (!file_exists($source))
        {
            throw new logicexception('File or folder ' . var_export($source, true) . ' is missing.');
        }
        
        if (file_exists($target))
        {
            throw new logicexception('File or folder ' . var_export($target, true) . ' already exist.');
        }
        
        if (is_dir($source))
        {
            self::create($target);
            $folder = dir($source);
            
            if (!$folder)
            {
                throw new unexpectedvalueexception('Folder could not be opened.');
            }
            
            while (($file = $folder->read()) !== false)
            {
                if ($file === '.' || $file === '..')
                {
                    continue;
                }
                
                self::copy($source . '/' . $file, $target . '/' . $file);
            }
            
            $folder->close();
        }
        else
        {
            if (!copy($source, $target))
            {
                throw new unexpectedvalueexception('File ' . var_export($source, true) . ' could not be copied to ' . var_export($copy, true) . '.');
            }
        }
    }

    /**
     * Moves a file or folder.
     * 
     * @param string $source The file or folder name.
     * @param string $target The location name.
     * @param boolean (optional) $uploaded Whether the file is a user uploaded
     * file or not.
     * @throws logicexception If the source file or folder is missing.
     * @throws logicexception If the target file or folder already exist.
     * @throws unexpectedvalueexception If the file or folder could not be
     * moved.
     */
    public static function move($source, $target, $uploaded = false)
    {
        if (!file_exists($source))
        {
            throw new logicexception('File or folder ' . var_export($source, true) . ' is missing.');
        }
        
        if (is_dir($source))
        {
            if (file_exists($target))
            {
                throw new logicexception('Folder ' . var_export($target, true) . ' already exist.');
            }
            
            if (!rename($source, $target))
            {
                throw new unexpectedvalueexception('Folder ' . var_export($source, true) . ' could not be moved to ' . var_export($target, true) . '.');
            }
        }
        else
        {
            if (is_dir($target))
            {
                $target = trim($target, '/') . '/' . basename($source);
                
                if (file_exists($target))
                {
                    throw new logicexception('File ' . var_export($target, true) . ' already exist.');
                }
            }
            
            if ($uploaded)
            {
                if (!move_uploaded_file($source, $target))
                {
                    throw new unexpectedvalueexception('Uploaded file ' . var_export($source, true) . ' could not be moved to ' . var_export($target, true) . '.');
                }
            }
            else
            {
                if (!rename($source, $target))
                {
                    throw new unexpectedvalueexception('File ' . var_export($source, true) . ' could not be renamed to ' . var_export($target, true) . '.');
                }
            }
        }
    }

    /**
     * Changes a file or folder permissions.
     * 
     * @param string $source The file or folder name.
     * @param integer $mode The permissisons.
     * @param boolean (optinal) $recursive Whether to change mode of files
     * inside the folder or not.
     * @throws logicexception If the file or folder is missing.
     * @throws unexpectedvalueexception If the file or folder permission mode
     * could not be changed.
     * @throws unexpectedvalueexception If the folder could not be opened.
     */
    public static function mode($source, $mode, $recursive = false)
    {
        if (!file_exists($source))
        {
            throw new logicexception('Folder or file ' . var_export($source, true) . ' is missing.');
        }
        
        if (!chmod($source, $mode))
        {
            throw new unexpectedvalueexception('Folder or file ' . var_export($source, true) . ' permission mode could not be changed.');
        }
        
        if ($recursive && is_dir($source))
        {
            $folder = dir($source);
            
            if (!$folder)
            {
                throw new unexpectedvalueexception('Folder could not be opened.');
            }
            
            while (($item = $folder->read()) !== false)
            {
                if ($file === '.' || $file === '..')
                {
                    continue;
                }
                
                self::mode($source . '/' . $file, $mode, $recursive);
            }
            
            $folder->close();
        }
    }

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