Skip to content

Commit

Permalink
Merge pull request #517 from NiR-/1.x-fs-exceptions
Browse files Browse the repository at this point in the history
Introduce StorageFailure and FileNotFound exceptions
  • Loading branch information
nicolasmure authored Jul 7, 2017
2 parents 6882e53 + faa268b commit 8d47c88
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 22 deletions.
29 changes: 24 additions & 5 deletions src/Gaufrette/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace Gaufrette;

use Gaufrette\Exception\FileNotFound;
use Gaufrette\Exception\StorageFailure;

/**
* Interface for the filesystem adapters.
*
Expand All @@ -15,7 +18,10 @@ interface Adapter
*
* @param string $key
*
* @return string|bool if cannot read content
* @return string
*
* @throws FileNotFound
* @throws StorageFailure If the underlying storage fails (adapter should not leak exceptions)
*/
public function read($key);

Expand All @@ -25,7 +31,9 @@ public function read($key);
* @param string $key
* @param string $content
*
* @return int|bool The number of bytes that were written into the file
* @return int The number of bytes that were written into the file
*
* @throws StorageFailure If the underlying storage fails (adapter should not leak exceptions)
*/
public function write($key, $content);

Expand All @@ -35,13 +43,17 @@ public function write($key, $content);
* @param string $key
*
* @return bool
*
* @throws StorageFailure If the underlying storage fails (adapter should not leak exceptions)
*/
public function exists($key);

/**
* Returns an array of all keys (files and directories).
*
* @return array
*
* @throws StorageFailure If the underlying storage fails (adapter should not leak exceptions)
*/
public function keys();

Expand All @@ -50,7 +62,10 @@ public function keys();
*
* @param string $key
*
* @return int|bool An UNIX like timestamp or false
* @return int An UNIX like timestamp
*
* @throws FileNotFound
* @throws StorageFailure If the underlying storage fails (adapter should not leak exceptions)
*/
public function mtime($key);

Expand All @@ -59,7 +74,8 @@ public function mtime($key);
*
* @param string $key
*
* @return bool
* @throws FileNotFound
* @throws StorageFailure If the underlying storage fails (adapter should not leak exceptions)
*/
public function delete($key);

Expand All @@ -69,7 +85,8 @@ public function delete($key);
* @param string $sourceKey
* @param string $targetKey
*
* @return bool
* @throws FileNotFound
* @throws StorageFailure If the underlying storage fails (adapter should not leak exceptions)
*/
public function rename($sourceKey, $targetKey);

Expand All @@ -79,6 +96,8 @@ public function rename($sourceKey, $targetKey);
* @param string $key
*
* @return bool
*
* @throws StorageFailure If the underlying storage fails (adapter should not leak exceptions)
*/
public function isDirectory($key);
}
81 changes: 64 additions & 17 deletions src/Gaufrette/Adapter/AwsS3.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

namespace Gaufrette\Adapter;

use Aws\S3\Exception\S3Exception;
use Gaufrette\Adapter;
use Aws\S3\S3Client;
use Gaufrette\Exception\FileNotFound;
use Gaufrette\Exception\StorageFailure;
use Gaufrette\Util;

/**
Expand Down Expand Up @@ -49,6 +52,9 @@ public function __construct(S3Client $service, $bucket, array $options = [], $de
$options
);

// Remove trailing slash so it can't be doubled in computePath() method
$this->options['directory'] = rtrim($this->options['directory'], '/');

$this->detectContentType = $detectContentType;
}

Expand Down Expand Up @@ -94,7 +100,11 @@ public function read($key)

return (string) $object->get('Body');
} catch (\Exception $e) {
return false;
if ($e instanceof S3Exception && $e->getResponse()->getStatusCode() === 404) {
throw new FileNotFound($key);
}

throw StorageFailure::unexpectedFailure('read', ['key' => $key], $e);
}
}

Expand All @@ -114,7 +124,15 @@ public function rename($sourceKey, $targetKey)

return $this->delete($sourceKey);
} catch (\Exception $e) {
return false;
if ($e instanceof S3Exception && $e->getResponse()->getStatusCode() === 404) {
throw new FileNotFound($sourceKey);
}

throw StorageFailure::unexpectedFailure(
'rename',
['sourceKey' => $sourceKey, 'targetKey' => $targetKey],
$e
);
}
}

Expand Down Expand Up @@ -143,7 +161,11 @@ public function write($key, $content)

return Util\Size::fromContent($content);
} catch (\Exception $e) {
return false;
throw StorageFailure::unexpectedFailure(
'write',
['key' => $key, 'content' => $content],
$e
);
}
}

Expand All @@ -152,7 +174,13 @@ public function write($key, $content)
*/
public function exists($key)
{
return $this->service->doesObjectExist($this->bucket, $this->computePath($key));
$path = $this->computePath($key);

try {
return $this->service->doesObjectExist($this->bucket, $path);
} catch (\Exception $exception) {
throw StorageFailure::unexpectedFailure('exists', ['key' => $key], $exception);
}
}

/**
Expand All @@ -165,7 +193,11 @@ public function mtime($key)

return strtotime($result['LastModified']);
} catch (\Exception $e) {
return false;
if ($e instanceof S3Exception && $e->getResponse()->getStatusCode() === 404) {
throw new FileNotFound($key);
}

throw StorageFailure::unexpectedFailure('mtime', ['key' => $key], $e);
}
}

Expand All @@ -179,7 +211,11 @@ public function size($key)

return $result['ContentLength'];
} catch (\Exception $e) {
return false;
if ($e instanceof S3Exception && $e->getResponse()->getStatusCode() === 404) {
throw new FileNotFound($key);
}

throw StorageFailure::unexpectedFailure('size', ['key' => $key], $e);
}
}

Expand All @@ -198,17 +234,20 @@ public function listKeys($prefix = '')
{
$this->ensureBucketExists();

$options = ['Bucket' => $this->bucket];
if ((string) $prefix != '') {
$options['Prefix'] = $this->computePath($prefix);
} elseif (!empty($this->options['directory'])) {
$options['Prefix'] = $this->options['directory'];
}
$options = [
'Bucket' => $this->bucket,
'Prefix' => $this->computePath($prefix),
];

$keys = [];
$iter = $this->service->getIterator('ListObjects', $options);
foreach ($iter as $file) {
$keys[] = $this->computeKey($file['Key']);
$objects = $this->service->getIterator('ListObjects', $options);

try {
foreach ($objects as $file) {
$keys[] = $this->computeKey($file['Key']);
}
} catch (S3Exception $e) {
throw StorageFailure::unexpectedFailure('listKeys', ['prefix' => $prefix], $e);
}

return $keys;
Expand All @@ -224,7 +263,11 @@ public function delete($key)

return true;
} catch (\Exception $e) {
return false;
if ($e instanceof S3Exception && $e->getResponse()->getStatusCode() === 404) {
throw new FileNotFound($key);
}

throw StorageFailure::unexpectedFailure('delete', ['key' => $key], $e);
}
}

Expand Down Expand Up @@ -335,7 +378,11 @@ public function mimeType($key)
$result = $this->service->headObject($this->getOptions($key));
return ($result['ContentType']);
} catch (\Exception $e) {
return false;
if ($e instanceof S3Exception && $e->getResponse()->getStatusCode() === 404) {
throw new FileNotFound($key);
}

throw StorageFailure::unexpectedFailure('mimeType', ['key' => $key], $e);
}
}
}
6 changes: 6 additions & 0 deletions src/Gaufrette/Adapter/ChecksumCalculator.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace Gaufrette\Adapter;

use Gaufrette\Exception\FileNotFound;
use Gaufrette\Exception\StorageFailure;

/**
* Interface which add checksum calculation support to adapter.
*
Expand All @@ -15,6 +18,9 @@ interface ChecksumCalculator
* @param string $key
*
* @return string
*
* @throws FileNotFound
* @throws StorageFailure If the underlying storage fails (adapter should not leak exceptions)
*/
public function checksum($key);
}
4 changes: 4 additions & 0 deletions src/Gaufrette/Adapter/ListKeysAware.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Gaufrette\Adapter;

use Gaufrette\Exception\StorageFailure;

/**
* interface that adds support of native listKeys to adapter.
*
Expand All @@ -16,6 +18,8 @@ interface ListKeysAware
* @param string $prefix
*
* @return array
*
* @throws StorageFailure If the underlying storage fails (adapter should not leak exceptions)
*/
public function listKeys($prefix = '');
}
6 changes: 6 additions & 0 deletions src/Gaufrette/Adapter/MimeTypeProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace Gaufrette\Adapter;

use Gaufrette\Exception\FileNotFound;
use Gaufrette\Exception\StorageFailure;

/**
* Interface which add mime type provider support to adapter.
*
Expand All @@ -15,6 +18,9 @@ interface MimeTypeProvider
* @param string $key
*
* @return string
*
* @throws FileNotFound
* @throws StorageFailure If the underlying storage fails (adapter should not leak exceptions)
*/
public function mimeType($key);
}
5 changes: 5 additions & 0 deletions src/Gaufrette/Adapter/SizeCalculator.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?php

namespace Gaufrette\Adapter;
use Gaufrette\Exception\FileNotFound;
use Gaufrette\Exception\StorageFailure;

/**
* Interface which add size calculation support to adapter.
Expand All @@ -15,6 +17,9 @@ interface SizeCalculator
* @param string $key
*
* @return int
*
* @throws FileNotFound
* @throws StorageFailure If the underlying storage fails (adapter should not leak exceptions)
*/
public function size($key);
}
37 changes: 37 additions & 0 deletions src/Gaufrette/Exception/StorageFailure.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Gaufrette\Exception;

use Gaufrette\Exception;

/**
* Exception thrown when an unexpected error happened at the storage level (or its underlying sdk).
*
* @author Albin Kerouanton <[email protected]>
*/
class StorageFailure extends \RuntimeException implements Exception
{
/**
* Instantiate a new StorageFailure exception for a particular adapter action.
*
* @param string $action The adapter action (e.g read, write, listKeys, ...) that throw the exception.
* @param array $args Arguments used by the action (like the read key).
* @param \Exception|null $previous Previous exception, if any was thrown (like exception from AWS sdk).
*
* @return StorageFailure
*/
public static function unexpectedFailure($action, array $args, \Exception $previous = null)
{
$args = array_map(function ($k, $v) {
$v = is_string($v) ? '"'.$v.'"' : $v;

return "{$k}: {$v}";
}, array_keys($args), $args);

return new self(
sprintf('An unexpected error happened during %s (%s).', $action, implode(', ', $args)),
0,
$previous
);
}
}
2 changes: 2 additions & 0 deletions tests/Gaufrette/Functional/Adapter/AwsS3Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

class AwsS3Test extends FunctionalTestCase
{
use FileNotFoundTests;

/** @var int */
static private $SDK_VERSION;

Expand Down
Loading

0 comments on commit 8d47c88

Please sign in to comment.