Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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