Coder Social home page Coder Social logo

core's Introduction

Filicious high level object oriented filesystem abstraction for PHP

Build Status

This is a high level filesystem abstraction for php, inspired by the Java filesystem API.

Why another filesystem abstraction?

We evaluated various filesystem abstraction frameworks, like Gaufrette. But none of the frameworks we found, provides a real filesystem abstraction. Gaufrette for example is more a key => value storage, that uses a filesystem or online storage as source. Some essential functions, like deleting a directory are not available in Gaufrette. Copying files across filesystem adapters is also not possible.

The benefit of Filicious is that it is a unique layer that...

  • can be used every time you work with files (also for temporary files)
  • can be used across multiple filesystems (also move or copy files between one another)
  • is a nearly complete replacement for the php file API
  • does not hide the file structure
  • provides high and low level functions to the filesystem
  • works with php iterators
  • provides a "merged" filesystem, that builds a merged structure from several filesystems
  • supports streaming
  • provides configurable public url generation (useful for web apps)

Start with Filicious

use Filicious\Local\LocalAdapter;
use Filicious\Filesystem;

// go into your kitchen
$adapter = new LocalAdapter('/var/lib/kitchen');
$kitchen = new Filesystem($adapter);

// and grab the starter menu
$starterMenuInKitchen = $kitchen->getFile('/starter.menu');

// access the lounge
$adapter = new LocalAdapter('/var/lib/lounge');
$lounge  = new Filesystem($adapter);

// and move the starter menu from the kitchen to the lounge
$starterMenuInLounge = $lounge->getFile('/starter.menu');
$starterMenuInKitchen->moveTo($starterMenuInLounge);

Find out more on filicious.github.io/how-to-use.

core's People

Contributors

backbone87 avatar crnjakovic avatar discordier avatar hellpat avatar stamster avatar toflar avatar tristanlins avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

core's Issues

new vendor / project name

We are searching a new vendor / project name for the php-filesystem.
Feel free to make suggestions.

Collection of all usable names:

  • Path
  • Pathfinder
  • Filex
  • Filicious
  • Flix
  • Filix
  • suiff
  • supiff
  • swiff
  • pufi
  • UFO
  • Osmosis (or Osmosys to put IT flavor in)

The bold one is the current preference.

Introduce factories

Introduce factories that allow usage like this:

// --- init config

// Variante 1 (obsolet)
$config = new LocalFilesystemConfig();
$config->setBasePath("/var/www");

// Variante 2
$config = new FilesystemConfig();
$config->set(LocalFilesystem::CONFIG_BASEPATH, "/var/www");
$config->set(FilesystemConfig::FILESYSTEM, "Bit3\Filesystem\Local\LocalFilesystem");

// --- use factory

// Variante 1
$fs = FilesystemFactory::createFilesystem($config);

// Variante 2
$factory = FilesystemFactory::newFactory();
$factory->set(FilesystemFactory::CONFIG, $config);
$fs = $factory->createFilesystem();

// Variante 3
$factory = FilesystemFactory::newFactory();
$factory->set(FilesystemFactory::FILESYSTEM, "Bit3\Filesystem\Local\LocalFilesystem");
$factory->set(LocalFilesystem::CONFIG_BASEPATH, "/var/www");
$fs = $factory->createFilesystem();
$config = $fs->getConfig();

// --- direct use filesystem

// Variante 1
$fs = new LocalFilesystem($config);

inotify alternative

I would add observers to make it possible to listen to filesystem changes like inotify on *nix.

Inconsistent link support

The current link support depends on the native link support of the filesystem.
It cannot be abstracted, because the link may be broken in the abstraction.

How should filicious work around links?

The File interface is to bloated

The File interface has just to many methods.

getMIME* is not baseline.

I do not want to remove this features, just provide some less bloated approach when implementing.

Something like Plugins come into my mind.

$fs = create_file_system();
$fs->addPlugin(new MIMEPlugin());
$f = $fs->getFile('my_file');
assert($f->mime, 'application/octet-stream');

The good thing about this is, you can handle other metadata with that:

$fs->addPlugin(new GDPlugin());
$f = new File('my_image');
assert($f->width, 200);
assert($f->height, 200);
assert(is_ressource($f->gd), true);

PHPUnit fails on LocalFilesystemTest under WinXP

I have commented out all link related tests and the link creation in setup, since it is not supported on WinXP

1) Filicious\Local\LocalFilesystemTest::testGetRoot
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-file:C:\Dokumente und Einstellungen\backbone\Lokale Einstellungen\Temp\phpBC6.tmp
+file:c:/Dokumente und Einstellungen/backbone/Lokale Einstellungen/Temp/phpBC6.tmp

G:\workspaces\git\php-filesystem\test\Filicious\Local\LocalFilesystemTest.php:142

2) Filicious\Local\LocalFilesystemTest::testGetFile
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-file:C:\Dokumente und Einstellungen\backbone\Lokale Einstellungen\Temp\phpBC7.tmp/example.txt
+file:c:/Dokumente und Einstellungen/backbone/Lokale Einstellungen/Temp/phpBC7.tmp/example.txt

G:\workspaces\git\php-filesystem\test\Filicious\Local\LocalFilesystemTest.php:154

3) Filicious\Local\LocalFilesystemTest::testIsExecutable
Failed asserting that <boolean:false> is true.

G:\workspaces\git\php-filesystem\test\Filicious\Local\LocalFilesystemTest.php:748

Rework Config interface

class Config

class Config
{
    /** @return Config */
    public function commit();
    /** @return bool */
    public function changed();
    /** @return Config */
    public function discard();
    /** @return Config */
    public function select($name = null) {
        $name = strval($name);
        return strlen($name) ? new ConfigContext($this, $name) : $this;
    }
    /** @return string */
    public function getContext()
    /** @return Config */
    public function addAdapter($config, &$name = null, $path = null, $insert = null);
    /** @return Config */
    public function set($param, $value = null)
    /** @return mixed */
    public function get($param, $default = null, $inherit = false);
    /** @return bool */
    public function has($param, $inherit = false)
    /** @return Filesystem */
    public function getFilesystem()
    /** @return Iterator [$name => $config] */
    public function getAdapters()
    public function serialize()
    public function unserialize($str)
    public function __clone()
    public function __toString()
}

class ConfigContext

class ConfigContext extends Config
{
    /** @var string */
    protected $context;

    /** @var Config */
    protected $config;

    /** @return Config */
    public function commit()
    {
        return $this->config->commit();
    }
    /** @return bool */
    public function changed()
    {
        return $this->config->changed();
    }
    /** @return Config */
    public function discard()
    {
        return $this->config->discard();
    }
    /** @return Config */
    public function select($name = null)
    {
        $name = strval($name);
        return $name === $this->context ? $this : $this->config->select($name);
    }
    /** @return string */
    public function getContext()
    {
        return $this->context;
    }
    /** @return Config */
    public function addAdapter($config, &$name = null, $path = null, $insert = null)
    {
        return $this->config->addAdapter(
            $config,
            $name,
            $path,
            $insert
        );
    }
    /** @return Config */
    public function set($param, $value = null)
    {
        return $this->config->set(
            $param,
            $value
        );
    }
    /** @return mixed */
    public function get($param, $default = null, $inherit = false)
    {
        return $this->config->set(
            $param,
            $default,
            $inherit
        );
    }
    /** @return bool */
    public function has($param, $inherit = false)
    {
        return $this->config->has(
            $param,
            $inherit
        );
    }
    /** @return Filesystem */
    public function getFilesystem()
    {
        return $this->config->getFilesystem();
    }
    /** @return Iterator [$name => $config] */
    public function getAdapters()
    {
        return $this->config->getAdapters();
    }
    public function serialize()
    {
        return $this->config->serialize();
    }
    public function unserialize($str)
    {
        return $this->config->unserialize($str);
    }
    public function __clone()
    {
        return $this->config->__clone();
    }
    public function __toString()
    {
        return $this->config->__toString();
    }
}

Make the File implementations more lightweight

By sharing one basic File implementation accross all (most) Filesystem implementations.

I do not want to change the interfaces. Only the implementation (specifically the AbstractFile and AbstractFilesystem implementation).

For example filesize:
The Filesystem determines how big a file behind a pathname is

public abstract function AbstractFilesystem::getSize(File $file);
public function BaseFile::__construct(AbstractFilesystem $fs);
public function BaseFile::getSize() { return $this->fs->getSize($this); }

although clearify's the abstraction:
The File object just abstracts a pathname within a specific filesystem.
The Filesystem should decide how to access/manipulate/retrieve meta data from a pathname.

support rm -rf

We need support for force deletion without modifying the mode flags.

Completly remove PublicURLProvider

@see #38 (comment)

  • Drop File::getRealURL()
  • Drop File::getPublicURL()
  • Introduce Filesystem::getFileURL(File $file)
  • Introduce File::toURL() (candy only)

Reason:
Generating a URL (whether public or not) is completly dependant on the filesystem implementation, so you would need a separate PublicURLProvider implementation for each Filesystem implementation.

Remapping a (possibly publicy-reachable) URL produced by a filesystem for a file it contains, is subject to the host-application in use.

File::isAccessable

@see #60 (comment)

The accessability differ between files and directories (in a unix permission environment).

A file is accessable if it is readable.
A directory is accessable if it is executeable and readable (for content listing).

How about add a convenience method File::isAccessable:

class File::isAccessable {
    public function isAccessable() {
        if ($this->isFile()) {
            return $this->isReadable();
        }
        else if ($this->isDirectory()) {
            return $this->isExecuteable() && $this->isReadable();
        }
    }
}

According to #49 this may be extracted to the permission interface, to allow the permission manager check the accessablility.

Detect FS abilities

There should be methods to check FS abilities.
Like Filesystem::isLinkSupported, Filesystem::isMetaDataSupported, Filesystem::isStreamingSupported ...

listFiles must honor globbing better

listFiles() should not fetch all files and filter them after then.

For slow FS this is plain evil.

i.e.: glob = lib/*

FTP fetches all dirs in root but could rule all except fot /lib out forehand.

SimpleFilesystem methods names

The SimpleFilesystem methods use three forms of modification of the File method names:

  • nofix: delete, copyTo, ...
  • infix: isThisFile, isThisLink, ...
  • postfix: getSizeOf, getModeOf, ...

This is not a good approach.

Options:

  • use same names as File
  • use only one name transformation accross all methods, proposed prefix every File related method with file*

File Mode / Permissions Interface

Currently we do not work around different permission systems.
How about to introduce an interface and some classes to handle this problem.

interface FileMode // or FilePermissions
{
    const TYPE = 'unknown';

    /**
     * Check if file modes are supported by the fs.
     *
     * @return bool
     */
    public function isSupported();

    /**
     * Test if file is readable for the current user.
     * Which "user" is referenced, depends on the implementation
     * and have to be automatically detected.
     *
     * @return bool
     */
    public function isReadable();

    /**
     * Test if file is writeable for the current user.
     * Which "user" is referenced, depends on the implementation
     * and have to be automatically detected.
     *
     * @return bool
     */
    public function isWriteable();

    /**
     * Test if file is executeable for the current user.
     * Which "user" is referenced, depends on the implementation
     * and have to be automatically detected.
     *
     * @return bool
     */
    public function isExecuteable();

    /**
     * For stacked file permission systems, get the next hop.
     *
     * @return FileMode
     */
    public function nextFileMode();
}

Then we have multiple classes with specific methods.
The UnsupportedFileMode class that indicate that file permissions are not supported.

class UnsupportedFileMode implements FileMode
{
    const TYPE = 'unsupported';

    public function isSupported()
    {
        return false;
    }
}

The PrimitiveFileMode only know read, write, executeable. It does not know owner, groups or complex acl rules.

class PrimitiveFileMode implements FileMode
{
    const TYPE = 'primitive';

    public function isSupported()
    {
        return true;
    }
    public function isReadable();
    public function isWriteable();
    public function isExecuteable();
}

The UnixFileMode only know read, write, executeable for owner, groups and others.

class UnixFileMode implements FileMode
{
    const TYPE = 'unix';

    public function isSupported()
    {
        return true;
    }
    public function isOwnerReadable();
    public function isOwnerWriteable();
    public function isOwnerExecuteable();
    public function isGroupReadable();
    public function isGroupWriteable();
    public function isGroupExecuteable();
    public function isOthersReadable();
    public function isOthersWriteable();
    public function isOthersExecuteable();
    public function isSticky();
    // maybe more
}

As you expected, the ACLFileMode will allow access to acl rules.

An interface system like this allow us to respect the different permission system.
The FileMode::isSupported is mandatory, because in mounted or merged filesystem I don't have access to the underlaying filesystem to check the "file permissions" ability. The UnsupportedFileMode prevents File::getMode from returning null.

Also other systems are allowed to implement there own permission system. For example Contao could implement it's own ContaoPermissionsFileMode for example.

class ContaoPermissionsFileMode implements FileMode
{
    const TYPE = 'contao';

    public function isSupported()
    {
        return true;
    }
    public function isUserReadable($backendUserId);
    public function isUserWriteable($backendUserId);
    public function isMemberReadable($memberId);
    public function isMemberWriteable($memberId);
}

To allow access to different permission systems, for example the ContaoPermissionsFileMode is just an overlay over UnixFileMode, the File::getMode should be extended with a type parameter:

interface File
{
    /**
     * Get permission access object for this file.
     *
     * @param string $type The permission system to access.
     * If null the filesystem default permission system will be returned.
     *
     * @throw UnsupportedPermissionSystemException will be thrown if the requested permission system is not available.
     */
    public function getMode($type = null);
}

To access different permission systems just use the TYPE constant:

/** @var ContaoFilesystem $fs */
$file = $fs->getFile('foo');

/** @var ContaoPermissionsFileMode $contaoPerms */
$contaoPerms = $file->getMode();

// forcing a permission system (not good)
try {
    /** @var UnixFileMode $unixPerms */
    $unixPerms = $file->getMode(UnixFileMode::TYPE);
} catch (UnsupportedPermissionSystemException $e) {
    // unix unsupported, maybe it is a linked in remote share like dropbox or amazon S3
}

// get the next permission system (good way)
/** @var UnixFileMode|DropboxFileMode|AmazonS3FileMode|... $nextPerms */
$nextPerms = $contaoPerms->nextFileMode();

This allow us to transparent add file permission systems, nobody knows about it, but it will protect the files. The File::isReadable, File::isWriteable and File::isExecuteable should be shortcuts to the FileMode::isReadable, FileMode::isWriteable, FileMode::isExecuteable methods.

Support for meta informations

The script should add an interface for storing meta data to a ressource. This would prevent plugins to implement hter own interfaces.

Upper/LowerCase handling

Some FileSystems doesn´t care about capitalization.

For Windows is "info" & "Info" the same.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.