Skip to content

Commit

Permalink
Merge pull request #7933 from ewinslow/filesystem
Browse files Browse the repository at this point in the history
chore(filesystem): Introduce `Directory` interface
  • Loading branch information
ewinslow committed Feb 14, 2015
2 parents b9b97d4 + 3da6153 commit 256df5b
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 217 deletions.
80 changes: 80 additions & 0 deletions engine/classes/Elgg/Filesystem/Directory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php
namespace Elgg\Filesystem;

/**
* A simple directory abstraction.
*
* @since 1.10.2
*
* @access private
*/
interface Directory {

/**
* Returns a subdirectory with access limited to the given directory.
*
* @param string $path The path relative to this directory to chroot to.
*
* @return Directory A new directory instance.
*/
public function chroot($path);

/**
* Read the file off the filesystem.
*
* @param string $path The directory-relative path to the target file.
*
* @return string Empty string if the file doesn't exist.
*/
public function getContents($path);

/**
* A reference to the file at the given path, even if it doesn't exist yet.
*
* However, will throw an exception if the file is already a directory.
*
* @param string $path The path to the file, relative to this directory.
*
* @return File
*/
public function getFile($path);

/**
* Recursively list the files in the given directory path.
*
* @param string $path The subdirectory path within this directory
*
* @return Collection<File>
*/
public function getFiles($path = '');

/**
* Do a PHP include of the file and return the result.
*
* NB: This only really works with local filesystems amirite?
*
* @param string $path Filesystem-relative path for the file to include.
*
* @return mixed
*/
public function includeFile($path);

/**
* Whether this directory has an existing file at the given location.
*
* @param string $path The relative path within this directory
*
* @return boolean
*/
public function isFile($path);

/**
* Write a file, overwriting the contents if necessary.
*
* @param string $path The path to the file.
* @param string $content The literal text content of the file.
*
* @return void
*/
public function putContents($path, $content);
}
38 changes: 18 additions & 20 deletions engine/classes/Elgg/Filesystem/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,34 @@
/**
* Represents a file that may or may not actually exist.
*
* @package Elgg.Core
* @subpackage Filesystem
* @since 1.10.0
* @since 1.10.0
*
* @access private
*/
class File {

/** @var Filesystem */
private $filesystem;
/** @var Directory */
private $directory;

/** @var string */
private $path;

/**
* Constructor
*
* @param Filesystem $filesystem The filesystem
* @param string $path The path to this file in the filesystem
* @param Directory $directory The directory where this file resides
* @param string $path The path to this file relative to the directory
*/
public function __construct(Filesystem $filesystem, $path) {
$this->filesystem = $filesystem;
public function __construct(Directory $directory, $path) {
$this->directory = $directory;
$this->path = $path;
}

/**
* @return boolean Whether this file exists.
*/
public function exists() {
return $this->filesystem->isFile($this->path);
}

/**
* @return string The file's extension.
*/
public function getExtension() {
return pathinfo($this->path, PATHINFO_EXTENSION);
return $this->directory->isFile($this->path);
}

/**
Expand All @@ -56,18 +47,25 @@ public function getBasename() {
* @return string
*/
public function getContents() {
return $this->filesystem->getFileContents($this->path);
return $this->directory->getContents($this->path);
}

/**
* @return string The file's extension.
*/
public function getExtension() {
return pathinfo($this->path, PATHINFO_EXTENSION);
}

/**
* Do a PHP include of the file and return the result.
*
* TODO: This may only work for local filesystem?
* TODO(ewinslow): This may only work for local filesystems?
*
* @return mixed
*/
public function includeFile() {
return $this->filesystem->includeFile($this->path);
return $this->directory->includeFile($this->path);
}

/** @inheritDoc */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
<?php
namespace Elgg\Filesystem;

use Elgg\Structs\ArrayCollection;
use Gaufrette\Adapter\Local;
use Gaufrette\Adapter\InMemory;
use Gaufrette\Filesystem as Gaufrette;

/**
* A simple filesystem abstraction.
* A wrapper around Gaufrette that implements Elgg's filesystem API.
*
* We've worked really hard to privatize the fact that this uses Gaufrette under the hood.
* This allows us to switch it out with something later if we need to.
* Changes to this class should maintain this level of privacy.
*
* @package Elgg.Core
* @subpackage Filesystem
* @since 1.10.0
* @since 1.10.0
*
* @access private
*/
final class Filesystem {
final class GaufretteDirectory implements Directory {

/** @var Gaufrette */
private $gaufrette;
Expand All @@ -42,16 +37,9 @@ private function __construct(Gaufrette $gaufrette, $localPath = '', $chroot = ''
$this->chroot = $this->normalize($chroot);
}

/**
* Whether this filesystem has an existing file at the given location.
*
* @param string $path The relative path within this filesystem
*
* @return boolean
*/
public function isFile($path) {
return !$this->isDirectory($path) &&
$this->gaufrette->has($this->getGaufrettePath($path));
/** @inheritDoc */
public function chroot($path) {
return new self($this->gaufrette, $this->localPath, $this->getGaufrettePath($path));
}

/**
Expand All @@ -66,69 +54,45 @@ private function isDirectory($path) {
return $adapter->isDirectory($this->getGaufrettePath($path));
}

/**
* A reference to the file at the given path, even if it doesn't exist yet.
*
* However, will throw an exception if the file is already a directory.
*
* @param string $path The path to the file, relative to this filesystem.
*
* @return File
*/
public function getFile($path) {
if ($this->isDirectory($path)) {
throw new \RuntimeException("There is already a directory at that location: $path");
}

return new File($this, $path);
/** @inheritDoc */
public function isFile($path) {
return !$this->isDirectory($path) &&
$this->gaufrette->has($this->getGaufrettePath($path));
}

/**
* Read the file off the filesystem.
*
* @param string $path The filesyste-relative path to the target file.
*
* @return string Empty string if the file doesn't exist.
*/
public function getFileContents($path) {
/** @inheritDoc */
public function getContents($path) {
try {
return $this->gaufrette->read($this->getGaufrettePath($path));
} catch (\Exception $e) {
return '';
}
}

/**
* Recursively list the files in the given directory path.
*
* @param string $path The directory path on this filesystem
*
* @return File[]
*/
/** @inheritDoc */
public function getFile($path) {
if ($this->isDirectory($path)) {
throw new \RuntimeException("There is already a directory at that location: $path");
}

return new File($this, $path);
}

/** @inheritDoc */
public function getFiles($path = '') {
$keys = $this->gaufrette->listKeys($this->getGaufrettePath($path));

$filesystem = $this;
return array_map(function($path) use ($filesystem) {
return new File($filesystem, $path);
}, $keys['keys']);
$files = new ArrayCollection($keys['keys']);

return $files->map(function($path) {
return new File($this, $path);
});
}

/**
* Get a path suitable for passing to the underlying gaufrette filesystem.
* Get the absolute path to the given directory-relative path.
*
* @param string $path The path relative to this filesystem.
*
* @return string
*/
private function getGaufrettePath($path) {
return $this->normalize("$this->chroot/$path");
}

/**
* Get the absolute path to the given filesystem-relative path.
*
* @param string $path A file/directory path within this filesystem.
* @param string $path A file/directory path within this directory.
*
* @return string
*/
Expand All @@ -137,54 +101,22 @@ private function getFullPath($path = '') {
return "$this->localPath/$gaufrettePath";
}


/**
* Do a PHP include of the file and return the result.
* This only really works with local filesystems amirite?
*
* @param string $path Filesystem-relative path for the file to include.
*
* @return mixed
*/
public function includeFile($path) {
return include $this->getFullPath($path);
}

/**
* Whether there is a file or directory at the given path.
*
* @param string $path Filesystem-relative path to check for existence.
*
* @return boolean
*/
private function exists($path) {
return $this->gaufrette->has($this->getGaufrettePath($path));
}

/**
* Write a file, overwriting if necessary.
* Get a path suitable for passing to the underlying gaufrette filesystem.
*
* @param string $path The path to the file.
* @param string $content The literal text content of the file.
* @param string $path The path relative to this directory.
*
* @return void
* @return string
*/
public function put($path, $content) {
$this->gaufrette->write($this->getGaufrettePath($path), $content, true);
private function getGaufrettePath($path) {
return $this->normalize("$this->chroot/$path");
}

/**
* Returns a new filesystem with access limited to the given directory.
*
* @param string $path The path relative to this filesystem to chroot to.
*
* @return Filesystem A new filesystem instance.
*/
public function chroot($path) {
return new Filesystem($this->gaufrette, $this->localPath, $this->getGaufrettePath($path));
/** @inheritDoc */
public function includeFile($path) {
return include $this->getFullPath($path);
}


/**
* Get a standardized form of the given path to work with internally.
*
Expand All @@ -196,6 +128,11 @@ private function normalize($path) {
return trim($path, "/");
}

/** @inheritDoc */
public function putContents($path, $content) {
$this->gaufrette->write($this->getGaufrettePath($path), $content, true);
}

/**
* Shorthand for generating a new local filesystem.
*
Expand All @@ -204,7 +141,7 @@ private function normalize($path) {
* @return Filesystem
*/
public static function createLocal($path) {
return new Filesystem(new Gaufrette(new Local($path)), $path);
return new self(new Gaufrette(new Local($path)), $path);
}

/**
Expand All @@ -213,6 +150,6 @@ public static function createLocal($path) {
* @return Filesystem
*/
public static function createInMemory() {
return new Filesystem(new Gaufrette(new InMemory()));
return new self(new Gaufrette(new InMemory()));
}
}

0 comments on commit 256df5b

Please sign in to comment.