|
VirtualFilesystems
Explains how to create custom filesystems
OverviewSabreDAV is built to easily adapt existing business logic onto a virtual network filesystem. This document explores how this can be setup.
High-level APISabreDAV is shipped with an API that should ease creating directory-tree structures. The SabreDAV library is built with the following 3 layers: Server layerThe Server class deals with the inner workings of the protocol. It maps all the low-level HTTP operations to a Tree class. Tree layerThe Tree class is responsible for returning File and Directory objects based on a path. As a starting point it can be easier to just always use the ObjectTree object, which has all the functionality already implemented. It can be benefitial to roll your own if your architecture allows optmizations in requesting objects or copying files. File/Directory layerThis manual will focus on implementing a virtual filesystem on this layer. Files and DirectoriesFiles and Directories both implement the Sabre_DAV_INode interface, this interface dictates the following methods should be implemented:
Additionally File objects need to implement the following methods:
Directory objects add the following:
Inheritance treeNote: this tree is slightly simplified for clarification Sabre_DAV_INode (base interface for all nodes in a tree)
+-Sabre_DAV_IFile (base interface for all files)
| +-Sabre_DAV_File (base helper class)
|
+-Sabre_DAV_DIrectory (base interface for all directories)
+-Sabre_DAV_Directory (base helper class)
Next to the interfaces, there are two helper classes in this diagram (Sabre_DAV_File and Sabre_DAV_Directory). These classes are an easy starting point, as they will lock down most operations by default (by reporting 'permission denied'), so we can start with a read-only filesystem. ImplementationOur read-only filesystem is going to be based off the standard server filesystem. Normally it would be better to use something like the apache module mod_dav for this, but it will allow us to easily explain the concepts. Getting the classes readyFor this demonstration we need to create 2 classes, one for a directory and one for a file. We'll start out with the Directory class
class MyDirectory extends Sabre_DAV_Directory {
private $myPath;
function __construct($myPath) {
$this->myPath = $myPath;
}
function getChildren() {
$children = array();
// Loop through the directory, and create objects for each node
foreach(scandir($this->myPath) as $node) {
// Ignoring files staring with .
if ($node[0]==='.') continue;
$children[] = $this->getChild($node);
}
return $children;
}
function getChild($name) {
$path = $this->myPath . '/' . $name;
// We have to throw a FileNotFound exception if the file didn't exist
if (!file_exists($this->myPath)) throw new Sabre_DAV_Exception_FileNotFound('The file with name: ' . $name . ' could not be found');
// Some added security
if ($name[0]=='.') throw new Sabre_DAV_Exception_FileNotFound('Access denied');
if (is_dir($path)) {
return new MyDirectory($path);
} else {
return new MyFile($path);
}
}
function getName() {
return basename($this->myPath);
}
}In the example is shown the absolute minimum of methods that need to be implemented in order to create a read-only directory. I'm hoping the code will speak for itself. Same goes for the MyFile class class MyFile extends Sabre_DAV_File {
private $myPath;
function __construct($myPath) {
$this->myPath = $myPath;
}
function getName() {
return basename($this->myPath);
}
function get() {
return fopen($this->myPath,'r');
}
function getSize() {
return filesize($this->myPath);
}
}
It's important thing to note is, that you should usually not pass strings around. Although the get() method can just return a string, especially with larger files it's recommended to use streams (as shown with fopen). The put() and createFile() methods will always get a readable stream resource as arguments. Setting upI'm explaining the usage of your newly created server through code comments
// Make sure there is a directory in your current directory named 'public'. We will be exposing that directory to WebDAV
$publicDir = new MyDirectory('public');
// Now we create an ObjectTree, which dispatches all requests to your newly created file system
$objectTree = new Sabre_DAV_ObjectTree($publicDir);
// The object tree needs in turn to be passed to the server class
$server = new Sabre_DAV_Server($objectTree);
// We're required to set the base uri, it is recommended to put your webdav server on a root of a domain
$server->setBaseUri('/');
// And off we go!
$server->exec();
This is not virtualThats right! This is where you come in. You can make your MyFile and MyDirectory classes completely independent from the actual underlying filesystem. The list of items returned from getChildren could be a list of blogposts, and the 'get' method could return html data. Write supportIn order to get writing/modification support you should implement all the remaining methods. A good example of a completely built-out system like this can be found in the Sabre_DAV_FS directory. This system should closely mimic apache's mod_dav. Implementation of these is up to you (and optional) and is not written out in this manual, because at this point this should be fairly simple. However, this is not enough. OS/X Finder and DavFS will demand you add locking support to your filesystem. Locking supportLocking helps insuring no 2 people can overwrite each others changes. WebDAV has a system to accomodate locking. The simplest way to implement locking, is to use the Locks Plugin. Simply attach the Lock Manager to your Object Tree class, and you're off. The last example is extended to add a lock manager.
// Make sure there is a directory in your current directory named 'public'. We will be exposing that directory to WebDAV
$publicDir = new MyDirectory('public');
// Now we create an ObjectTree, which dispatches all requests to your newly created file system
$objectTree = new Sabre_DAV_ObjectTree($publicDir);
$objectTree->setLockManager($lockManager);
// The object tree needs in turn to be passed to the server class
$server = new Sabre_DAV_Server($objectTree);
// We're required to set the base uri, it is recommended to put your webdav server on a root of a domain
$server->setBaseUri('/');
// Also make sure there is a 'data' directory, writable by the server. This directory is used to store information about locks
$lockManager = new Sabre_DAV_Locks_Backend_FS('data');
$lockPlugin = new Sabre_DAV_Locks_Plugin($lockManager);
$server->addPlugin($lockPlugin);
// And off we go!
$server->exec();
|
Sign in to add a comment
The function MyDirectory?::getChild function is wrong when it returns directories, instead of passing the name, it should pass the path, e.g.:
if (is_dir($path)) { return new MyDirectory($path); } else { return new MyFile($path); }Thanks sir, fixed.