Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@ $publicMountPoint = new \Cmp\Storage\MountPoint('/var/www/app/public', $fallBack
$vfs->registerMountPoint($localMountPoint);
$vfs->registerMountPoint($publicMountPoint);

/*

//move file from /tmp (FS) to /var/www/app/public (S3) and if fails try to move from /tmp (FS) to /var/www/app/public (FS)
*/
$vfs->move('/tmp/testfile.jpg','/var/www/app/public/avatar.jpg' );
```

Expand Down Expand Up @@ -139,7 +138,7 @@ __Fluid calls:__
* `setStrategy(AbstractStorageCallStrategy $strategy)` : Set a custom strategy
* `setLogger(LoggerInterface $logger)` : Set custom logger
* `addAdapter($adapter)` : Add a new adapter
* `build(AbstractStorageCallStrategy $callStrategy = null, LoggerInterface $logger = null)` : Build the virtual storage
* `build(AbstractStorageCallStrategy $callStrategy = null)` : Build the virtual storage

__Non fluid calls:__

Expand Down
239 changes: 147 additions & 92 deletions src/Cmp/Storage/Adapter/FileSystemAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,79 @@

namespace Cmp\Storage\Adapter;

use Cmp\Storage\AdapterInterface;
use Cmp\Storage\Exception\FileExistsException;
use Cmp\Storage\Exception\FileNotFoundException;
use Cmp\Storage\Exception\InvalidPathException;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\LogLevel;
use Psr\Log\NullLogger;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;

/**
* Class FileSystemAdapter.
*/
class FileSystemAdapter implements \Cmp\Storage\AdapterInterface
class FileSystemAdapter implements AdapterInterface, LoggerAwareInterface
{
use LoggerAwareTrait;
use LogicalChecksTrait;

/**
* Adapter Name.
*/
const NAME = 'FileSystem';
const MAX_PATH_SIZE = 255; //The major part of fs has this limit

public function __construct()
{
$this->logger = new NullLogger();
}

/**
* Read a file.
*
* @param string $path The path to the file
*
* @throws FileNotFoundException
*
* @return string The file contents or false on failure
*/
public function get($path)
{
$path = $this->normalizePath($path);
$this->assertNotFileExists($path);

return file_get_contents($path);
}

private function normalizePath($path)
{
$this->assertFileMaxLength($path);

return realpath($path);
}

/**
* @param $path
*
* @throws InvalidPathException
*/
private function assertFileMaxLength($path)
{
if (strlen(basename($path)) > self::MAX_PATH_SIZE) {
$e = new InvalidPathException($path);
$this->logger->log(
LogLevel::ERROR,
'Adapter "'.$this->getName().'" fails. Invalid path {path}.',
['exception' => $e, 'path' => $path]
);

throw $e;
}
}

/**
* Get Adapter name.
*
Expand All @@ -28,42 +86,44 @@ public function getName()
}

/**
* Check whether a file exists.
*
* @param string $path
* @param $path
*
* @return bool
* @throws FileNotFoundException
*/
public function exists($path)
private function assertNotFileExists($path)
{
$path = $this->normalizePath($path);
if (!$this->exists($path) || !is_file($path)) {
$e = new FileNotFoundException($path);
$this->logger->log(
LogLevel::ERROR,
'Adapter "'.$this->getName().'" fails. File {path} not exists.',
['exception' => $e, 'path' => $path]
);

return file_exists($path);
throw $e;
}
}

/**
* Read a file.
*
* @param string $path The path to the file
* Check whether a file exists.
*
* @throws \Cmp\Storage\FileNotFoundException
* @param string $path
*
* @return string The file contents or false on failure
* @return bool
*/
public function get($path)
public function exists($path)
{
$path = $this->normalizePath($path);
$this->assertNotFileExists($path);

return file_get_contents($path);
return file_exists($path);
}

/**
* Retrieves a read-stream for a path.
*
* @param string $path The path to the file
*
* @throws \Cmp\Storage\FileNotFoundException
* @throws FileNotFoundException
*
* @return resource The path resource or false on failure
*/
Expand All @@ -78,8 +138,8 @@ public function getStream($path)
/**
* Rename a file.
*
* @param string $path Path to the existing file
* @param string $newpath The new path of the file
* @param string $path Path to the existing file
* @param string $newpath The new path of the file
* @param bool $overwrite
*
* @return bool Thrown if $newpath exists
Expand All @@ -89,9 +149,7 @@ public function getStream($path)
public function rename($path, $newpath, $overwrite = false)
{
$path = $this->normalizePath($path);
if (!$overwrite && $this->exists($newpath)) {
throw new FileExistsException($newpath);
}
$this->ensureWeCanWriteDestFile($newpath, $overwrite);
$this->assertNotFileExists($path);

return rename($path, $newpath);
Expand All @@ -100,8 +158,8 @@ public function rename($path, $newpath, $overwrite = false)
/**
* Copy a file.
*
* @param string $path Path to the existing file
* @param string $newpath The destination path of the copy
* @param string $path Path to the existing file
* @param string $newpath The destination path of the copy
*
* @return bool
*/
Expand Down Expand Up @@ -134,6 +192,28 @@ public function delete($path)
}
}

/**
* Removes directory recursively.
*
* @param string $path
*
* @return bool
*/
private function removeDirectory($path)
{
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);

foreach ($files as $fileinfo) {
$todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink');
$todo($fileinfo->getRealPath());
}

return rmdir($path);
}

/**
* Create a file or update if exists. It will create the missing folders.
*
Expand All @@ -147,12 +227,8 @@ public function delete($path)
public function put($path, $contents)
{
$this->assertFileMaxLength($path);
if (is_dir($path)) {
throw new InvalidPathException($path);
}
if (!$this->createParentFolder($path)) {
throw new InvalidPathException($path);
}
$this->assertIsDir($path);
$this->ensureParentPathExists($path);
if (($size = file_put_contents($path, $contents)) === false) {
return false;
}
Expand All @@ -161,52 +237,45 @@ public function put($path, $contents)
}

/**
* Create a file or update if exists. It will create the missing folders.
*
* @param string $path The path to the file
* @param resource $resource The file handle
*
* @return bool
* @param $path
*
* @throws InvalidPathException
*/
public function putStream($path, $resource)
private function assertIsDir($path)
{
$this->assertFileMaxLength($path);
if (is_dir($path)) {
throw new InvalidPathException($path);
}
if (!$this->createParentFolder($path)) {
throw new InvalidPathException($path);
}
$stream = fopen($path, 'w+');
$e = new InvalidPathException($path);

if (!$stream) {
return false;
}

stream_copy_to_stream($resource, $stream);
$this->logger->log(
LogLevel::ERROR,
'Adapter "'.$this->getName().'" fails. Path {path} is a directory.',
['exception' => $e, 'path' => $path]
);

return fclose($stream);
throw $e;
}
}

/**
* @param $path
*
* @throws FileNotFoundException
* @throws InvalidPathException
*/
private function assertNotFileExists($path)
private function ensureParentPathExists($path)
{
if (!$this->exists($path) || !is_file($path)) {
throw new FileNotFoundException($path);
}
}
if (!$this->createParentFolder($path)) {
$e = new InvalidPathException($path);

private function normalizePath($path)
{
$this->assertFileMaxLength($path);
$this->logger->log(
LogLevel::ERROR,
'Adapter "'.
$this->getName().
'" fails. Parent path {path} is not ready and it\'s impossible to create it.',
['exception' => $e, 'path' => $path]
);

return realpath($path);
throw $e;
}
}

/**
Expand All @@ -225,43 +294,29 @@ private function createParentFolder($path)
}

/**
* @param $path
*
* @throws InvalidPathException
*/
private function assertFileMaxLength($path)
{
if (strlen(basename($path)) > self::MAX_PATH_SIZE) {
throw new InvalidPathException($path);
}
}

/**
* Removes directory recursively.
* Create a file or update if exists. It will create the missing folders.
*
* @param string $path
* @param string $path The path to the file
* @param resource $resource The file handle
*
* @return bool
*
* @throws InvalidPathException
*/
private function removeDirectory($path)
public function putStream($path, $resource)
{
if (is_dir($path)) {
$objects = scandir($path);
foreach ($objects as $object) {
if ($object != '.' && $object != '..') {
if (is_dir($path.'/'.$object)) {
if (!$this->removeDirectory($path.'/'.$object)) {
return false;
}
} else {
if (!unlink($path.'/'.$object)) {
return false;
}
}
}
}

return rmdir($path);
$this->assertFileMaxLength($path);
$this->assertIsDir($path);
$this->ensureParentPathExists($path);

$stream = fopen($path, 'w+');

if (!$stream) {
return false;
}

stream_copy_to_stream($resource, $stream);

return fclose($stream);
}
}
Loading