first commit

This commit is contained in:
Roman Pyrih
2023-07-24 08:30:51 +02:00
commit c2e100a763
7128 changed files with 1622619 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[*.yml]
indent_size = 2

View File

@@ -0,0 +1,56 @@
FROM php:5.6-alpine
ENV SRC_DIR /usr/src/gaufrette
RUN apk add --no-cache --virtual .persistent-deps \
git \
zlib
# PHP extensions
ENV MONGODB_VERSION="1.2.11" \
SSH2_VERSION="0.13"
RUN set -xe \
&& apk add --no-cache --virtual .build-deps \
$PHPIZE_DEPS \
libssh2-dev \
zlib-dev \
&& docker-php-ext-install \
zip \
&& pecl install \
mongodb-${MONGODB_VERSION} \
ssh2-${SSH2_VERSION} \
&& docker-php-ext-enable --ini-name 05-opcache.ini opcache \
&& docker-php-ext-enable \
mongodb \
ssh2 \
&& apk del .build-deps
COPY docker/php.ini /usr/local/etc/php/php.ini
COPY docker/install-composer.sh /usr/local/bin/install-composer
RUN chmod +x /usr/local/bin/install-composer
RUN set -xe \
&& install-composer \
&& mv composer.phar /usr/local/bin/composer
ENV COMPOSER_ALLOW_SUPERUSER 1
RUN composer global require "hirak/prestissimo" --prefer-dist --no-progress --no-suggest --optimize-autoloader --apcu-autoloader \
&& composer clear-cache
WORKDIR ${SRC_DIR}
COPY composer.json ./
RUN composer update --prefer-dist --no-autoloader --no-scripts --no-progress --no-suggest \
&& composer clear-cache
COPY spec spec/
COPY src src/
COPY tests tests/
COPY bin/tests bin/tests
RUN composer dump-autoload
CMD ["bin/tests"]

View File

@@ -0,0 +1,68 @@
FROM php:7.0-alpine
ENV SRC_DIR /usr/src/gaufrette
RUN apk add --no-cache --virtual .persistent-deps \
git \
zlib
# PHP extensions
ENV MONGODB_VERSION="1.2.11" \
SSH2_VERSION="1.1.2"
RUN set -xe \
&& apk add --no-cache --virtual .build-deps \
$PHPIZE_DEPS \
libssh2-dev \
zlib-dev \
&& docker-php-ext-install \
zip \
&& pecl install \
mongodb-${MONGODB_VERSION} \
ssh2-${SSH2_VERSION} \
&& docker-php-ext-enable --ini-name 05-opcache.ini opcache \
&& docker-php-ext-enable \
mongodb \
ssh2 \
&& apk del .build-deps
COPY docker/php.ini /usr/local/etc/php/php.ini
COPY docker/install-composer.sh /usr/local/bin/docker-gaufrette-install-composer
RUN chmod +x /usr/local/bin/docker-gaufrette-install-composer
RUN set -xe \
&& apk add --no-cache --virtual .fetch-deps \
openssl \
&& docker-gaufrette-install-composer \
&& mv composer.phar /usr/local/bin/composer \
&& apk del .fetch-deps
COPY docker/php.ini /usr/local/etc/php/php.ini
COPY docker/install-composer.sh /usr/local/bin/install-composer
RUN chmod +x /usr/local/bin/install-composer
RUN set -xe \
&& install-composer \
&& mv composer.phar /usr/local/bin/composer
ENV COMPOSER_ALLOW_SUPERUSER 1
RUN composer global require "hirak/prestissimo" --prefer-dist --no-progress --no-suggest --optimize-autoloader --apcu-autoloader \
&& composer clear-cache
WORKDIR ${SRC_DIR}
COPY composer.json ./
RUN composer update --prefer-dist --no-autoloader --no-scripts --no-progress --no-suggest \
&& composer clear-cache
COPY spec spec/
COPY src src/
COPY tests tests/
COPY bin/tests bin/tests
RUN composer dump-autoload
CMD ["bin/tests"]

View File

@@ -0,0 +1,56 @@
FROM php:7.1-alpine
ENV SRC_DIR /usr/src/gaufrette
RUN apk add --no-cache --virtual .persistent-deps \
git \
zlib
# PHP extensions
ENV MONGODB_VERSION="1.2.11" \
SSH2_VERSION="1.1.2"
RUN set -xe \
&& apk add --no-cache --virtual .build-deps \
$PHPIZE_DEPS \
libssh2-dev \
zlib-dev \
&& docker-php-ext-install \
zip \
&& pecl install \
mongodb-${MONGODB_VERSION} \
ssh2-${SSH2_VERSION} \
&& docker-php-ext-enable --ini-name 05-opcache.ini opcache \
&& docker-php-ext-enable \
mongodb \
ssh2 \
&& apk del .build-deps
COPY docker/php.ini /usr/local/etc/php/php.ini
COPY docker/install-composer.sh /usr/local/bin/install-composer
RUN chmod +x /usr/local/bin/install-composer
RUN set -xe \
&& install-composer \
&& mv composer.phar /usr/local/bin/composer
ENV COMPOSER_ALLOW_SUPERUSER 1
RUN composer global require "hirak/prestissimo" --prefer-dist --no-progress --no-suggest --optimize-autoloader --apcu-autoloader \
&& composer clear-cache
WORKDIR ${SRC_DIR}
COPY composer.json ./
RUN composer update --prefer-dist --no-autoloader --no-scripts --no-progress --no-suggest \
&& composer clear-cache
COPY spec spec/
COPY src src/
COPY tests tests/
COPY bin/tests bin/tests
RUN composer dump-autoload
CMD ["bin/tests"]

View File

@@ -0,0 +1,22 @@
Copyright (c) 2010 Antoine Hérault
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,68 @@
build: false
platform: 'x86'
clone_folder: C:\projects\gaufrette
branches:
except:
- gh-pages
cache:
- '%LOCALAPPDATA%\Composer\files'
- c:\php -> appveyor.yml
environment:
AWS_KEY:
secure: nMVfvhojgVc9VBlrYXeddaYIQHlVAlupWmxNjXMlsB0=
AWS_SECRET:
secure: MiAf2q7mnqSIZSoChd9v3pwUrXHeAEpZP8Zc0E0qjiOBXPvplzJVtrkWRwTNlQl3
AWS_BUCKET: gaufretteci
RACKSPACE_USER:
secure: aEIZdm+IXe9dGLOnSmgu8A==
RACKSPACE_APIKEY:
secure: CqBlB4EVIM2FMaYxBdplTaQzw8JgKElxYvKYpXUXPHpOdwJcnYy0gk2KdjF4fXDM
RACKSPACE_CONTAINER: gaufretteci
AZURE_ACCOUNT:
secure: s6JCZ8ztVGvVYprceONZOg==
AZURE_KEY:
secure: 6+9kd9n/BO65LI8ZCJJXOwo9v5QmMULKWExHgECe+rnflaw+ZiQ8jFfHwxxjOfUF1qulAyxHQmDj7M3Cfvbe/DAASjk52ngEMvHC6wv2/Eo0cBMQiUlS9vPeUlvkUy1L
AZURE_CONTAINER: gaufretteci
MONGO_URI: "mongodb://127.0.0.1:27017"
MONGO_DBNAME: "gridfs_test"
services:
- mongodb
init:
- SET PATH=C:\Program Files\OpenSSL;c:\php;%PATH%
- SET PHP=1
install:
- IF EXIST c:\php (SET PHP=0) ELSE (mkdir c:\php)
- cd c:\php
- IF %PHP%==1 cinst -y OpenSSL.Light
- IF %PHP%==1 curl -fsSL -o php-5.6.33-Win32-VC11-x86.zip https://windows.php.net/downloads/releases/archives/php-5.6.33-Win32-VC11-x86.zip
- IF %PHP%==1 7z x php-5.6.33-Win32-VC11-x86.zip -y >nul
- IF %PHP%==1 del /Q *.zip
- IF %PHP%==1 curl -fsSL -o cacert.pem https://curl.haxx.se/ca/cacert.pem
- IF %PHP%==1 cd ext
- IF %PHP%==1 curl -fsSL -o php_mongodb-1.2.9-5.6-ts-vc11-x86.zip https://windows.php.net/downloads/pecl/releases/mongodb/1.2.9/php_mongodb-1.2.9-5.6-ts-vc11-x86.zip
- IF %PHP%==1 7z x php_mongodb-1.2.9-5.6-ts-vc11-x86.zip php_mongodb.dll -y >nul
- IF %PHP%==1 del /Q *.zip
- IF %PHP%==1 cd ..
- IF %PHP%==1 copy php.ini-production php.ini /Y
- IF %PHP%==1 echo extension_dir=ext >> php.ini
- IF %PHP%==1 echo date.timezone="UTC" >> php.ini
- IF %PHP%==1 echo extension=php_mbstring.dll >> php.ini
- IF %PHP%==1 echo extension=php_openssl.dll >> php.ini
- IF %PHP%==1 echo extension=php_mongodb.dll >> php.ini
- IF %PHP%==1 echo extension=php_pdo_sqlite.dll >> php.ini
- IF %PHP%==1 echo extension=php_fileinfo.dll >> php.ini
- IF %PHP%==1 echo extension=php_curl.dll >> php.ini
- IF %PHP%==1 echo openssl.cafile=c:\php\cacert.pem >> php.ini
- cd C:\projects\gaufrette
- curl -fsSL -o composer.phar https://getcomposer.org/composer.phar
- php composer.phar update --prefer-dist --no-interaction --no-progress --no-suggest --ansi
test_script:
- cd C:\projects\gaufrette
- vendor\bin\phpspec run -fpretty --verbose
- vendor\bin\phpunit --verbose -c phpunit.xml.dist

View File

@@ -0,0 +1,10 @@
#!/bin/sh
set -o nounset
set -o errexit
set -o xtrace
DIR=$(dirname $0)/..
${DIR}/vendor/bin/phpunit tests
${DIR}/vendor/bin/phpspec run --format=pretty --no-code-generation

View File

@@ -0,0 +1,7 @@
#!/bin/sh
set -o xtrace
docker-compose run --rm php56
docker-compose run --rm php70
docker-compose run --rm php71

View File

@@ -0,0 +1,3 @@
FROM stilliard/pure-ftpd
RUN (echo gaufrette; echo gaufrette) | pure-pw useradd gaufrette -f /etc/pure-ftpd/passwd/pureftpd.passwd -m -u ftpuser -d /home/ftpusers/gaufrette

View File

@@ -0,0 +1,21 @@
#!/bin/sh
# Copyright (c) Nils Adermann, Jordi Boggiano
# Origin: https://github.com/composer/composer/blob/master/doc/faqs/how-to-install-composer-programmatically.md
# Licence: MIT
EXPECTED_SIGNATURE=$(wget -q -O - https://composer.github.io/installer.sig)
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
ACTUAL_SIGNATURE=$(php -r "echo hash_file('SHA384', 'composer-setup.php');")
if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]
then
>&2 echo 'ERROR: Invalid installer signature'
rm composer-setup.php
exit 1
fi
php composer-setup.php --quiet
RESULT=$?
rm composer-setup.php
exit $RESULT

View File

@@ -0,0 +1,2 @@
apc.enable_cli = 1
date.timezone = UTC

View File

@@ -0,0 +1,2 @@
extensions:
Akeneo\SkipExampleExtension: Akeneo\SkipExampleExtension

View File

@@ -0,0 +1,238 @@
<?php
namespace spec\Gaufrette\Adapter;
use PhpSpec\ObjectBehavior;
class AclAwareAmazonS3Spec extends ObjectBehavior
{
/**
* @param \Gaufrette\Adapter $adapter
* @param \AmazonS3 $service
*/
function let($adapter, $service)
{
$this->beConstructedWith($adapter, $service, 'bucketName');
}
function it_is_adapter()
{
$this->shouldHaveType('Gaufrette\Adapter');
}
function it_supports_metadata()
{
$this->shouldHaveType('Gaufrette\Adapter\MetadataSupporter');
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_delegates_read($adapter)
{
$adapter->read('filename')->willReturn('some content');
$adapter->read('filename2')->willReturn('other content');
$this->read('filename')->shouldReturn('some content');
$this->read('filename2')->shouldReturn('other content');
}
/**
* @param \Gaufrette\Adapter $adapter
* @param \AmazonS3 $service
*/
function it_delegates_rename_and_update_acl($adapter, $service)
{
$service
->set_object_acl('bucketName', 'filename2', \AmazonS3::ACL_PRIVATE)
->shouldBeCalled()
->willReturn(new \CFResponse(array(), '', 200))
;
$adapter
->rename('filename', 'filename2')
->shouldBeCalled()
->willReturn(true)
;
$adapter
->delete('filename')
->shouldNotBeCalled()
;
$this->rename('filename', 'filename2')->shouldReturn(true);
}
/**
* @param \Gaufrette\Adapter $adapter
* @param \AmazonS3 $service
*/
function it_does_not_rename_when_cannot_update_acl($adapter, $service)
{
$service
->set_object_acl('bucketName', 'filename2', \AmazonS3::ACL_PRIVATE)
->shouldBeCalled()
->willReturn(new \CFResponse(array(), '', 500));
$adapter
->rename('filename', 'filename2')
->shouldBeCalled()
->willReturn(true);
$adapter
->delete('filename2')
->shouldBeCalled();
$this->rename('filename', 'filename2')->shouldReturn(false);
}
/**
* @param \Gaufrette\Adapter $adapter
* @param \AmazonS3 $service
*/
function it_updates_acl_with_users_array_when_rename($adapter, $service)
{
$service
->set_object_acl('bucketName', 'filename2', array(array('id' => 'someId', 'permission' => \AmazonS3::GRANT_READ)))
->shouldBeCalled()
->willReturn(new \CFResponse(array(), '', 200))
;
$adapter
->rename('filename', 'filename2')
->willReturn(true)
;
$this->setUsers(array(array('id' => 'someId', 'permission' => 'read')));
$this->rename('filename', 'filename2')->shouldReturn(true);
}
/**
* @param \Gaufrette\Adapter $adapter
* @param \AmazonS3 $service
*/
function it_delegates_write_and_update_acl($adapter, $service)
{
$service
->set_object_acl('bucketName', 'filename', \AmazonS3::ACL_PRIVATE)
->shouldBeCalled()
->willReturn(new \CFResponse(array(), '', 200))
;
$adapter
->write('filename', 'some content')
->shouldBeCalled()
->willReturn(12)
;
$adapter
->delete('filename')
->shouldNotBeCalled()
;
$this->write('filename', 'some content')->shouldReturn(12);
}
/**
* @param \Gaufrette\Adapter $adapter
* @param \AmazonS3 $service
*/
function it_does_not_write_when_cannot_update_acl($adapter, $service)
{
$service
->set_object_acl('bucketName', 'filename', \AmazonS3::ACL_PRIVATE)
->shouldBeCalled()
->willReturn(new \CFResponse(array(), '', 500))
;
$adapter
->write('filename', 'some content')
->shouldBeCalled()
->willReturn(12)
;
$adapter
->delete('filename')
->shouldBeCalled()
;
$this->write('filename', 'some content')->shouldReturn(false);
}
/**
* @param \Gaufrette\Adapter $adapter
* @param \AmazonS3 $service
*/
function it_updates_acl_with_users_array_when_write($adapter, $service)
{
$service
->set_object_acl('bucketName', 'filename', array(array('id' => 'someId', 'permission' => \AmazonS3::GRANT_READ)))
->shouldBeCalled()
->willReturn(new \CFResponse(array(), '', 200))
;
$adapter
->write('filename', 'some content')
->willReturn(12)
;
$this->setUsers(array(array('id' => 'someId', 'permission' => 'read')));
$this->write('filename', 'some content')->shouldReturn(12);
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_delegates_exists($adapter)
{
$adapter->exists('filename')->willReturn(true);
$adapter->exists('filename2')->willReturn(false);
$this->exists('filename')->shouldReturn(true);
$this->exists('filename2')->shouldReturn(false);
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_delegates_mtime($adapter)
{
$adapter->mtime('filename')->willReturn(1234);
$adapter->mtime('filename2')->willReturn(2345);
$this->mtime('filename')->shouldReturn(1234);
$this->mtime('filename2')->shouldReturn(2345);
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_delegates_directory_check($adapter)
{
$adapter->isDirectory('filename')->willReturn(true);
$adapter->isDirectory('filename2')->willReturn(false);
$this->isDirectory('filename')->shouldReturn(true);
$this->isDirectory('filename2')->shouldReturn(false);
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_delegates_keys($adapter)
{
$adapter->keys()->willReturn(array('filename', 'filename2'));
$this->keys()->shouldReturn(array('filename', 'filename2'));
}
/**
* @param \spec\Gaufrette\Adapter\TestDelegateAdapter $extendedAdapter
* @param \AmazonS3 $service
*/
function it_delegates_metadata_handling($extendedAdapter, $service)
{
$this->beConstructedWith($extendedAdapter, $service, 'bucketName');
$extendedAdapter->setMetadata('filename', array('some'))->shouldBeCalled();
$extendedAdapter->getMetadata('filename')->shouldBeCalled()->willReturn(array('some2'));
$this->setMetadata('filename', array('some'));
$this->getMetadata('filename')->shouldReturn(array('some2'));
}
}
interface TestDelegateAdapter extends \Gaufrette\Adapter,
\Gaufrette\Adapter\MetadataSupporter
{
}

View File

@@ -0,0 +1,683 @@
<?php
namespace spec\Gaufrette\Adapter;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class AmazonS3Spec extends ObjectBehavior
{
/**
* @param \AmazonS3 $service
*/
function let($service)
{
$this->beConstructedWith($service, 'bucketName');
}
function it_is_adapter()
{
$this->shouldHaveType('Gaufrette\Adapter');
}
function it_supports_metadata()
{
$this->shouldHaveType('Gaufrette\Adapter\MetadataSupporter');
}
/**
* @param \AmazonS3 $service
*/
function it_reads_file($service)
{
$options = array(
'range' => 12,
'response' => array(
'content-language' => 'pl-pl'
)
);
$service
->if_bucket_exists('bucketName')
->shouldBeCalled()
->willReturn(true)
;
$service
->get_object(
'bucketName',
'filename',
$options
)
->shouldBeCalled()
->willReturn(new \CFResponse('header', 'some content', 200))
;
$this->setMetadata('filename', $options);
$this->read('filename')->shouldReturn('some content');
}
/**
* @param \AmazonS3 $service
*/
function it_returns_false_when_cannot_read($service)
{
$service
->if_bucket_exists('bucketName')
->shouldBeCalled()
->willReturn(true)
;
$service
->get_object(
'bucketName',
'filename',
array()
)
->shouldBeCalled()
->willReturn(new \CFResponse('header', 'some content', 500))
;
$this->read('filename')->shouldReturn(false);
}
/**
* @param \AmazonS3 $service
*/
function it_is_verbose_and_throws_exceptions_when_read($service)
{
$service
->if_bucket_exists('bucketName')
->shouldBeCalled()
->willReturn(true)
;
$service
->get_object(
'bucketName',
'filename',
array()
)
->willThrow(new \RuntimeException('read'))
;
$this->shouldThrow(new \RuntimeException('read'))->duringRead('filename');
}
/**
* @param \AmazonS3 $service
*/
function it_rename_file($service)
{
$service
->if_bucket_exists('bucketName')
->shouldBeCalled()
->willReturn(true)
;
$service
->copy_object(
array(
'bucket' => 'bucketName',
'filename' => 'filename1',
),
array(
'bucket' => 'bucketName',
'filename' => 'filename2'
),
array('acl' => \AmazonS3::ACL_OWNER_READ)
)
->shouldBeCalled()
->willReturn(new \CFResponse('header', 'some content', 200))
;
$service
->delete_object(
'bucketName',
'filename1',
Argument::any()
)
->shouldBeCalled()
->willReturn(new \CFResponse(array(), 'some', 200))
;
$this->setMetadata('filename1', array('acl' => \AmazonS3::ACL_OWNER_READ));
$this->rename('filename1', 'filename2')->shouldReturn(true);
}
/**
* @param \AmazonS3 $service
*/
function it_is_verbose_and_throws_exceptions_when_rename($service)
{
$service
->if_bucket_exists('bucketName')
->shouldBeCalled()
->willReturn(true)
;
$service
->copy_object(Argument::cetera())
->willThrow(new \RuntimeException('rename'))
;
$this->shouldThrow(new \RuntimeException('rename'))->duringRename('filename', 'filename1');
}
/**
* @param \AmazonS3 $service
*/
function it_returns_false_when_cannot_rename($service)
{
$service
->if_bucket_exists('bucketName')
->shouldBeCalled()
->willReturn(true)
;
$service
->copy_object(
array(
'bucket' => 'bucketName',
'filename' => 'filename1',
),
array(
'bucket' => 'bucketName',
'filename' => 'filename2'
),
array()
)
->shouldBeCalled()
->willReturn(new \CFResponse('header', 'some content', 500))
;
$this->rename('filename1', 'filename2')->shouldReturn(false);
}
/**
* @param \AmazonS3 $service
*/
function it_should_write_file($service)
{
$service
->if_bucket_exists('bucketName')
->shouldBeCalled()
->willReturn(true)
;
$service
->create_object(
'bucketName',
'filename',
array(
'acl' => \AmazonS3::ACL_PRIVATE,
'body' => 'some content'
)
)
->shouldBeCalled()
->willReturn(new \CFResponse(array('x-aws-requestheaders' => array('Content-Length' => 12)), 'some content', 200))
;
$this->setMetadata('filename', array('acl' => \AmazonS3::ACL_PRIVATE, 'body' => 'other content'));
$this->write('filename', 'some content')->shouldReturn(12);
}
/**
* @param \AmazonS3 $service
*/
function it_returns_false_when_cannot_write($service)
{
$service
->if_bucket_exists('bucketName')
->shouldBeCalled()
->willReturn(true)
;
$service
->create_object(
'bucketName',
'filename',
array(
'acl' => \AmazonS3::ACL_PUBLIC,
'body' => 'some content'
)
)
->shouldBeCalled()
->willReturn(new \CFResponse(array('x-aws-requestheaders' => array('Content-Length' => 12)), 'some content', 500))
;
$this->write('filename', 'some content')->shouldReturn(false);
}
/**
* @param \AmazonS3 $service
*/
function it_is_verbose_and_throws_exceptions_when_write($service)
{
$service
->if_bucket_exists('bucketName')
->shouldBeCalled()
->willReturn(true)
;
$service
->create_object(Argument::cetera())
->willThrow(new \RuntimeException('write'))
;
$this->shouldThrow(new \RuntimeException('write'))->duringWrite('filename', 'some content');
}
/**
* @param \AmazonS3 $service
*/
function it_should_check_if_file_exists($service)
{
$service
->if_bucket_exists('bucketName')
->shouldBeCalled()
->willReturn(true)
;
$service->if_object_exists('bucketName', 'filename')->willReturn(true);
$this->exists('filename')->shouldReturn(true);
$service->if_object_exists('bucketName', 'filename')->willReturn(false);
$this->exists('filename')->shouldReturn(false);
}
/**
* @param \AmazonS3 $service
*/
function it_is_verbose_and_throws_exceptions_when_file_exists($service)
{
$service
->if_bucket_exists('bucketName')
->shouldBeCalled()
->willReturn(true)
;
$service
->if_object_exists('bucketName', 'filename')
->willThrow(new \RuntimeException('exists'))
;
$this->shouldThrow(new \RuntimeException('exists'))->duringExists('filename');
}
/**
* @param \AmazonS3 $service
*/
function it_should_get_file_mtime($service)
{
$metadata = array('acl' => \AmazonS3::ACL_PUBLIC);
$service
->if_bucket_exists('bucketName')
->shouldBeCalled()
->willReturn(true)
;
$service
->get_object_metadata(
'bucketName',
'filename',
$metadata
)
->shouldBeCalled()
->willReturn(array('Headers' => array('last-modified' => '2012-01-01 23:10:10')))
;
$this->setMetadata('filename', $metadata);
$this->mtime('filename')->shouldReturn(strtotime('2012-01-01 23:10:10'));
}
/**
* @param \AmazonS3 $service
*/
function it_returns_false_when_cannot_fetch_mtime($service)
{
$service
->if_bucket_exists('bucketName')
->shouldBeCalled()
->willReturn(true)
;
$service
->get_object_metadata(
'bucketName',
'filename',
array()
)
->shouldBeCalled()
->willReturn(array('Headers' => array()))
;
$this->mtime('filename')->shouldReturn(false);
}
/**
* @param \AmazonS3 $service
*/
function it_is_verbose_and_throws_exceptions_when_fetch_mtime($service)
{
$service
->if_bucket_exists('bucketName')
->shouldBeCalled()
->willReturn(true)
;
$service
->get_object_metadata('bucketName', 'filename', Argument::any())
->willThrow(new \RuntimeException('mtime'))
;
$this->shouldThrow(new \RuntimeException('mtime'))->duringMtime('filename');
}
/**
* @param \AmazonS3 $service
*/
function it_should_delete_file($service)
{
$metadata = array('acl' => \AmazonS3::ACL_PRIVATE);
$service
->if_bucket_exists('bucketName')
->shouldBeCalled()
->willReturn(true)
;
$service
->delete_object(
'bucketName',
'filename',
$metadata
)
->willReturn(new \CFResponse(array(), 'some', 200))
;
$this->setMetadata('filename', $metadata);
$this->delete('filename')->shouldReturn(true);
}
/**
* @param \AmazonS3 $service
*/
function it_is_verbose_and_throws_exceptions_when_fetch_delete($service)
{
$service
->if_bucket_exists('bucketName')
->willReturn(true)
;
$service
->delete_object(
'bucketName',
'filename',
Argument::any()
)
->willThrow(new \RuntimeException('delete'))
;
$this->shouldThrow(new \RuntimeException('delete'))->duringDelete('filename');
}
/**
* @param \AmazonS3 $service
*/
function it_returns_false_when_cannot_delete($service)
{
$service
->if_bucket_exists('bucketName')
->shouldBeCalled()
->willReturn(true)
;
$service
->delete_object(
'bucketName',
'filename',
array()
)
->willReturn(new \CFResponse(array(), 'some', 500))
;
$this->delete('filename')->shouldReturn(false);
}
/**
* @param \AmazonS3 $service
*/
function it_should_get_keys($service)
{
$service
->if_bucket_exists('bucketName')
->shouldBeCalled()
->willReturn(true)
;
$service
->get_object_list('bucketName')
->shouldBeCalled()
->willReturn(array('filename2', 'aaa/filename', 'filename1'))
;
$this->keys()->shouldReturn(array('aaa', 'aaa/filename', 'filename1', 'filename2'));
}
/**
* @param \AmazonS3 $service
*/
function it_is_verbose_and_throws_exceptions_when_fetch_keys($service)
{
$service
->if_bucket_exists('bucketName')
->willReturn(true)
;
$service
->get_object_list('bucketName')
->willThrow(new \RuntimeException('keys'))
;
$this->shouldThrow(new \RuntimeException('keys'))->duringKeys();
}
/**
* @param \AmazonS3 $service
*/
function it_should_handle_dirs($service)
{
$service
->if_bucket_exists('bucketName')
->willReturn(true)
;
$service
->if_object_exists('bucketName', 'filename')
->shouldNotBeCalled()
;
$service
->if_object_exists('bucketName', 'filename/')
->shouldBeCalled()
->willReturn(false)
;
$service
->if_object_exists('bucketName', 'dirname/')
->willReturn(true)
;
$this->isDirectory('filename')->shouldReturn(false);
$this->isDirectory('dirname')->shouldReturn(true);
}
/**
* @param \AmazonS3 $service
*/
function it_should_fail_when_bucket_does_not_exist($service)
{
$service
->if_bucket_exists('bucketName')
->willReturn(false)
;
$this
->shouldThrow(new \RuntimeException('The configured bucket "bucketName" does not exist.'))
->duringRead('filename')
;
$this
->shouldThrow(new \RuntimeException('The configured bucket "bucketName" does not exist.'))
->duringWrite('filename', 'content')
;
$this
->shouldThrow(new \RuntimeException('The configured bucket "bucketName" does not exist.'))
->duringDelete('filename')
;
$this
->shouldThrow(new \RuntimeException('The configured bucket "bucketName" does not exist.'))
->duringExists('filename')
;
$this
->shouldThrow(new \RuntimeException('The configured bucket "bucketName" does not exist.'))
->duringMtime('filename')
;
$this
->shouldThrow(new \RuntimeException('The configured bucket "bucketName" does not exist.'))
->duringRename('filename', 'filename2')
;
$this
->shouldThrow(new \RuntimeException('The configured bucket "bucketName" does not exist.'))
->duringKeys()
;
}
/**
* @param \AmazonS3 $service
*/
function it_creates_bucket_if_create_mode_is_enabled($service)
{
$service->set_region(Argument::any())->shouldBeCalled();
$service
->if_bucket_exists('bucketName')
->willReturn(false)
;
$service->hostname = \AmazonS3::REGION_US_E1;
$service
->create_bucket('bucketName', \AmazonS3::REGION_US_E1)
->shouldBeCalled()
->willReturn(new \CFResponse(array(), 'created', 201))
;
$service
->if_object_exists('bucketName', 'filename')
->willReturn(false)
;
$this->beConstructedWith($service, 'bucketName', array('create' => true));
$this->exists('filename');
}
/**
* @param \AmazonS3 $service
*/
function it_fails_when_cannot_create_bucket($service)
{
$service
->if_bucket_exists('bucketName')
->willReturn(false)
;
$service
->create_bucket('bucketName', Argument::any())
->shouldBeCalled()
->willReturn(new \CFResponse(array(), 'created', 500))
;
$this->beConstructedWith($service, 'bucketName', array('create' => true));
$this
->shouldThrow(new \RuntimeException('Failed to create the configured bucket "bucketName".'))
->duringExists('filename')
;
}
/**
* @param \AmazonS3 $service
*/
function it_allows_to_configure_reqion($service)
{
$service
->if_bucket_exists('bucketName')
->willReturn(true)
;
$service
->set_region(\AmazonS3::REGION_EU_W1)
->shouldBeCalled()
;
$service
->if_object_exists('bucketName', 'filename')
->willReturn(true)
;
$this->beConstructedWith($service, 'bucketName', array('region' => \AmazonS3::REGION_EU_W1));
$this->exists('filename');
}
/**
* @param \AmazonS3 $service
*/
function it_allows_to_configure_region_for_bucket($service)
{
$service->set_region(Argument::any())->shouldBeCalled();
$service
->if_bucket_exists('bucketName')
->willReturn(false)
;
$service
->create_bucket('bucketName', \AmazonS3::REGION_EU_W1)
->shouldBeCalled()
->willReturn(new \CFResponse(array(), 'created', 201))
;
$service
->if_object_exists('bucketName', 'filename')
->willReturn(false)
;
$this->beConstructedWith($service, 'bucketName', array('create' => true, 'region' => \AmazonS3::REGION_EU_W1));
$this->exists('filename');
}
/**
* @param \AmazonS3 $service
*/
function it_allows_to_configure_acl($service)
{
$this->setAcl('123abc');
$service
->if_bucket_exists('bucketName')
->shouldBeCalled()
->willReturn(true)
;
$service
->create_object(
'bucketName',
'filename',
array(
'acl' => '123abc',
'body' => 'some content'
)
)
->shouldBeCalled()
->willReturn(new \CFResponse(array('x-aws-requestheaders' => array('Content-Length' => 12)), 'some content', 200))
;
$this->write('filename', 'some content')->shouldReturn(12);
$this->getAcl()->shouldBe('123abc');
}
/**
* @param \AmazonS3 $service
*/
function its_file_metadata_acl_are_more_important_than_global_acl_config($service)
{
$this->setAcl('123abc');
$service
->if_bucket_exists('bucketName')
->shouldBeCalled()
->willReturn(true)
;
$service
->create_object(
'bucketName',
'filename',
array(
'acl' => 'more important acl',
'body' => 'some content'
)
)
->shouldBeCalled()
->willReturn(new \CFResponse(array('x-aws-requestheaders' => array('Content-Length' => 12)), 'some content', 200))
;
$this->setMetadata('filename', array('acl' => 'more important acl'));
$this->write('filename', 'some content')->shouldReturn(12);
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace spec\Gaufrette\Adapter;
//hack - mock php built-in functions
require_once 'functions.php';
use PhpSpec\ObjectBehavior;
class ApcSpec extends ObjectBehavior
{
function let()
{
global $extensionLoaded;
$extensionLoaded = true;
$this->beConstructedWith('prefix-apc-test/');
}
function letgo()
{
global $extensionLoaded;
$extensionLoaded = null;
}
function it_is_adapter()
{
$this->shouldHaveType('Gaufrette\Adapter');
}
function it_reads_file()
{
$this->read('filename')->shouldReturn('prefix-apc-test/filename content');
$this->read('filename2')->shouldReturn('prefix-apc-test/filename2 content');
}
function it_writes_file()
{
$this->write('filename', 'some content')->shouldReturn(12);
$this->write('invalid', 'some content')->shouldReturn(false);
}
function it_deletes_file()
{
$this->delete('filename')->shouldReturn(true);
$this->delete('invalid')->shouldReturn(false);
}
function it_rename_file()
{
$this->rename('filename', 'aaa/filename2')->shouldReturn(true);
$this->rename('filename', 'invalid')->shouldReturn(false);
$this->rename('invalid', 'somename')->shouldReturn(false);
}
function it_checks_if_file_exists()
{
$this->exists('filename')->shouldReturn(true);
$this->exists('invalid')->shouldReturn(false);
}
function it_does_not_handles_directory()
{
$this->isDirectory('filename')->shouldReturn(false);
$this->isDirectory('invalid')->shouldReturn(false);
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace spec\Gaufrette\Adapter;
use Gaufrette\Adapter\MimeTypeProvider;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class AwsS3Spec extends ObjectBehavior
{
/**
* @param \Aws\S3\S3Client $service
*/
function let($service)
{
$this->beConstructedWith($service, 'bucketName');
}
function it_is_initializable()
{
$this->shouldHaveType('Gaufrette\Adapter\AwsS3');
}
function it_is_adapter()
{
$this->shouldHaveType('Gaufrette\Adapter');
}
function it_supports_metadata()
{
$this->shouldHaveType('Gaufrette\Adapter\MetadataSupporter');
}
function it_supports_sizecalculator()
{
$this->shouldHaveType('Gaufrette\Adapter\SizeCalculator');
}
function it_provides_mime_type()
{
$this->shouldHaveType(MimeTypeProvider::class);
}
}

View File

@@ -0,0 +1,494 @@
<?php
namespace spec\Gaufrette\Adapter;
use PHPSpec2\ObjectBehavior;
use WindowsAzure\Blob\Models\Blob;
use WindowsAzure\Common\ServiceException;
class AzureBlobStorage extends ObjectBehavior
{
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
*/
public function let($blobProxyFactory)
{
$this->beConstructedWith($blobProxyFactory, 'containerName');
}
public function it_should_be_initializable()
{
$this->shouldHaveType('Gaufrette\Adapter\AzureBlobStorage');
$this->shouldHaveType('Gaufrette\Adapter');
$this->shouldHaveType('Gaufrette\Adapter\MetadataSupporter');
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
* @param \WindowsAzure\Blob\Models\GetBlobResult $getBlobResult
*/
public function it_should_read_file($blobProxyFactory, $blobProxy, $getBlobResult)
{
$getBlobResult
->getContentStream()
->shouldBeCalled()
//azure blob content is handled as stream so we need to fake it
->willReturn(fopen('data://text/plain,some content','r'));
$blobProxy
->getBlob('containerName', 'filename')
->shouldBeCalled()
->willReturn($getBlobResult);
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$this->read('filename')->shouldReturn('some content');
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
*/
public function it_should_return_false_when_cannot_read($blobProxyFactory, $blobProxy)
{
$blobProxy
->getBlob('containerName', 'filename')
->shouldBeCalled()
->willThrow(new ServiceException(500));
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$this->read('filename')->shouldReturn(false);
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
*/
public function it_should_not_mask_exception_when_read($blobProxyFactory, $blobProxy)
{
$blobProxy
->getBlob('containerName', 'filename')
->shouldBeCalled()
->willThrow(new \RuntimeException('read'));
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$this->shouldThrow(new \RuntimeException('read'))->duringRead('filename');
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
*/
public function it_should_rename_file($blobProxyFactory, $blobProxy)
{
$blobProxy
->copyBlob('containerName', 'filename2', 'containerName', 'filename1')
->shouldBeCalled();
$blobProxy
->deleteBlob('containerName', 'filename1')
->shouldBeCalled();
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$this->rename('filename1', 'filename2')->shouldReturn(true);
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
*/
public function it_should_return_false_when_cannot_rename($blobProxyFactory, $blobProxy)
{
$blobProxy
->copyBlob('containerName', 'filename2', 'containerName', 'filename1')
->shouldBeCalled()
->willThrow(new ServiceException(500));
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$this->rename('filename1', 'filename2')->shouldReturn(false);
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
*/
public function it_should_not_mask_exception_when_rename($blobProxyFactory, $blobProxy)
{
$blobProxy
->copyBlob('containerName', 'filename2', 'containerName', 'filename1')
->shouldBeCalled()
->willThrow(new \RuntimeException('rename'));
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$this->shouldThrow(new \RuntimeException('rename'))->duringRename('filename1', 'filename2');
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
*/
public function it_should_write_file($blobProxyFactory, $blobProxy)
{
$blobProxy
->createBlockBlob('containerName', 'filename', 'some content',
\Mockery::type('\WindowsAzure\Blob\Models\CreateBlobOptions'))
->shouldBeCalled();
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$this->write('filename', 'some content')->shouldReturn(12);
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
*/
public function it_should_return_false_when_cannot_write($blobProxyFactory, $blobProxy)
{
$blobProxy
->createBlockBlob('containerName', 'filename', 'some content',
\Mockery::type('\WindowsAzure\Blob\Models\CreateBlobOptions'))
->willThrow(new ServiceException(500));
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$this->write('filename', 'some content')->shouldReturn(false);
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
*/
public function it_should_not_mask_exception_when_write($blobProxyFactory, $blobProxy)
{
$blobProxy
->createBlockBlob('containerName', 'filename', 'some content',
\Mockery::type('\WindowsAzure\Blob\Models\CreateBlobOptions'))
->willThrow(new \RuntimeException('write'));
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$this->shouldThrow(new \RuntimeException('write'))->duringWrite('filename', 'some content');
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
* @param \WindowsAzure\Blob\Models\GetBlobResult $getBlobResult
*/
public function it_should_check_if_file_exists($blobProxyFactory, $blobProxy, $getBlobResult)
{
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$blobProxy
->getBlob('containerName', 'filename')
->shouldBeCalled()
->willThrow(new ServiceException(404));
$this->exists('filename')->shouldReturn(false);
$blobProxy
->getBlob('containerName', 'filename2')
->shouldBeCalled()
->willReturn($getBlobResult);
$this->exists('filename2')->shouldReturn(true);
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
*/
public function it_should_not_mask_exception_when_check_if_file_exists($blobProxyFactory, $blobProxy)
{
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$blobProxy
->getBlob('containerName', 'filename')
->shouldBeCalled()
->willThrow(new \RuntimeException('exists'));
$this->shouldThrow(new \RuntimeException('exists'))->duringExists('filename');
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
* @param \WindowsAzure\Blob\Models\GetBlobPropertiesResult $getBlobPropertiesResult
* @param \WindowsAzure\Blob\Models\BlobProperties $blobProperties
*/
public function it_should_get_file_mtime($blobProxyFactory, $blobProxy, $getBlobPropertiesResult, $blobProperties)
{
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$blobProxy
->getBlobProperties('containerName', 'filename')
->shouldBeCalled()
->willReturn($getBlobPropertiesResult);
$getBlobPropertiesResult
->getProperties()
->shouldBeCalled()
->willReturn($blobProperties);
$blobProperties
->getLastModified()
->shouldBeCalled()
->willReturn(new \DateTime('1987-12-28 20:00:00'));
$this->mtime('filename')->shouldReturn(strtotime('1987-12-28 20:00:00'));
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
*/
public function it_should_return_false_when_cannot_mtime($blobProxyFactory, $blobProxy)
{
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$blobProxy
->getBlobProperties('containerName', 'filename')
->shouldBeCalled()
->willThrow(new ServiceException(500));
$this->mtime('filename')->shouldReturn(false);
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
*/
public function it_should_not_mask_exception_when_get_mtime($blobProxyFactory, $blobProxy)
{
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$blobProxy
->getBlobProperties('containerName', 'filename')
->shouldBeCalled()
->willThrow(new \RuntimeException('mtime'));
$this->shouldThrow(new \RuntimeException('mtime'))->duringMtime('filename');
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
*/
public function it_should_delete_file($blobProxyFactory, $blobProxy)
{
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$blobProxy
->deleteBlob('containerName', 'filename')
->shouldBeCalled();
$this->delete('filename')->shouldReturn(true);
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
*/
public function it_should_return_false_when_cannot_delete_file($blobProxyFactory, $blobProxy)
{
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$blobProxy
->deleteBlob('containerName', 'filename')
->shouldBeCalled()
->willThrow(new ServiceException(500));
$this->delete('filename')->shouldReturn(false);
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
*/
public function it_should_not_mask_exception_when_delete($blobProxyFactory, $blobProxy)
{
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$blobProxy
->deleteBlob('containerName', 'filename')
->shouldBeCalled()
->willThrow(new \RuntimeException('delete'));
$this->shouldThrow(new \RuntimeException('delete'))->duringDelete('filename');
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
* @param \WindowsAzure\Blob\Models\ListBlobsResult $listBlobResult
*/
public function it_should_get_keys($blobProxyFactory, $blobProxy, $listBlobResult)
{
$fileNames = array('aaa', 'aaa/filename', 'filename1', 'filename2');
$blobs = array();
foreach ($fileNames as $fileName) {
$blob = new Blob();
$blob->setName($fileName);
$blobs[] = $blob;
}
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$blobProxy
->listBlobs('containerName')
->shouldBeCalled()
->willReturn($listBlobResult);
$listBlobResult
->getBlobs()
->shouldBeCalled()
->willReturn($blobs);
$this->keys()->shouldReturn(array('aaa', 'aaa/filename', 'filename1', 'filename2'));
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
*/
public function it_should_not_mask_exception_when_get_keys($blobProxyFactory, $blobProxy)
{
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$blobProxy
->listBlobs('containerName')
->shouldBeCalled()
->willThrow(new \RuntimeException('keys'));
$this->shouldThrow(new \RuntimeException('keys'))->duringKeys();
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
*/
public function it_should_handle_dirs($blobProxyFactory, $blobProxy)
{
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$blobProxy
->getBlob('containerName', 'filename')
->shouldNotBeCalled();
$blobProxy
->getBlob('containerName', 'filename/')
->shouldBeCalled()
->willThrow(new ServiceException(404));
$blobProxy
->getBlob('containerName', 'dirname/')
->shouldBeCalled();
$this->isDirectory('filename')->shouldReturn(false);
$this->isDirectory('dirname')->shouldReturn(true);
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
*/
public function it_should_create_container($blobProxyFactory, $blobProxy)
{
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$blobProxy
->createContainer('containerName', null)
->shouldBeCalled();
$this->createContainer('containerName');
}
/**
* @param \Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param \WindowsAzure\Blob\Internal\IBlob $blobProxy
*/
public function it_should_fail_when_cannot_create_container($blobProxyFactory, $blobProxy)
{
$blobProxyFactory
->create()
->shouldBeCalled()
->willReturn($blobProxy);
$blobProxy
->createContainer('containerName', null)
->shouldBeCalled()
->willThrow(new ServiceException(500));
$this->shouldThrow(new \RuntimeException('Failed to create the configured container "containerName": 0 ().', null))->duringCreateContainer('containerName');
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace spec\Gaufrette\Adapter\AzureBlobStorage;
use PHPSpec2\ObjectBehavior;
class BlobProxyFactory extends ObjectBehavior
{
/**
* @param string $connectionString
*/
public function let($connectionString)
{
$this->beConstructedWith($connectionString);
}
public function it_should_be_initializable()
{
$this->shouldHaveType('Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactory');
$this->shouldHaveType('Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface');
}
}

View File

@@ -0,0 +1,156 @@
<?php
namespace spec\Gaufrette\Adapter;
use PhpSpec\ObjectBehavior;
class CacheSpec extends ObjectBehavior
{
/**
* @param \Gaufrette\Adapter $source
* @param \Gaufrette\Adapter $cache
*/
function let($source, $cache)
{
$this->beConstructedWith($source, $cache);
}
function it_is_adapter()
{
$this->shouldHaveType('Gaufrette\Adapter');
}
function it_supports_metadata()
{
$this->shouldHaveType('Gaufrette\Adapter\MetadataSupporter');
}
/**
* @param \spec\Gaufrette\Adapter\CacheTestExtendedAdapter $extendedSource
* @param \spec\Gaufrette\Adapter\CacheTestExtendedAdapter $extendedCache
*/
function it_handles_metadata_when_cached_adapters_supports_metadata($extendedSource, $extendedCache)
{
$extendedSource->setMetadata('filename', array('metadata'))->shouldBeCalled();
$extendedCache->setMetadata('filename', array('metadata'))->shouldBeCalled();
$extendedSource->getMetadata('filename')->shouldBeCalled()->willReturn(array('someMetadata'));
$this->beConstructedWith($extendedSource, $extendedCache);
$this->setMetadata('filename', array('metadata'));
$this->getMetadata('filename')->shouldReturn(array('someMetadata'));
}
/**
* @param \Gaufrette\Adapter $source
*/
function it_delegates_is_directory_check_to_source($source)
{
$source->isDirectory('filename')->shouldBeCalled()->willReturn(true);
$this->isDirectory('filename')->shouldReturn(true);
}
/**
* @param \Gaufrette\Adapter $source
* @param \Gaufrette\Adapter $cache
*/
function it_reads_from_cache_adapter($source, $cache)
{
$source->read('filename')->shouldNotBeCalled();
$cache->read('filename')->shouldBeCalled()->willReturn('some content');
$source->mtime('filename')->willReturn(strtotime('2010-10-11'));
$cache->mtime('filename')->willReturn(strtotime('2010-10-12'));
$cache->exists('filename')->willReturn(true);
$this->read('filename')->shouldReturn('some content');
}
/**
* @param \Gaufrette\Adapter $source
* @param \Gaufrette\Adapter $cache
*/
function it_update_cache_adapter_when_source_file_is_modified($source, $cache)
{
$source->read('filename')->shouldBeCalled()->willReturn('some other content');
$cache->read('filename')->shouldNotBeCalled();
$cache->write('filename', 'some other content')->shouldBeCalled();
$source->mtime('filename')->willReturn(strtotime('2010-10-11'));
$cache->mtime('filename')->willReturn(strtotime('2010-10-10'));
$cache->exists('filename')->willReturn(true);
$this->read('filename')->shouldReturn('some other content');
}
/**
* @param \Gaufrette\Adapter $source
* @param \Gaufrette\Adapter $cache
*/
function it_rename_file_in_source_and_cache($source, $cache)
{
$source->rename('filename', 'filename2')->shouldBeCalled()->willReturn(true);
$cache->rename('filename', 'filename2')->shouldBeCalled()->willReturn(true);
$this->rename('filename', 'filename2')->shouldReturn(true);
}
/**
* @param \Gaufrette\Adapter $source
* @param \Gaufrette\Adapter $cache
*/
function it_writes_file_to_source_and_cache($source, $cache)
{
$source->write('filename', 'some content')->shouldBeCalled()->willReturn(12);
$cache->write('filename', 'some content')->shouldBeCalled()->willReturn(12);
$this->write('filename', 'some content')->shouldReturn(12);
}
/**
* @param \Gaufrette\Adapter $source
* @param \Gaufrette\Adapter $cache
*/
function it_deletes_file_from_source_and_cache($source, $cache)
{
$source->delete('filename')->shouldBeCalled()->willReturn(true);
$cache->delete('filename')->shouldBeCalled()->willReturn(true);
$this->delete('filename')->shouldReturn(true);
}
/**
* @param \Gaufrette\Adapter $source
*/
function it_check_if_exists_in_source($source)
{
$source->exists('filename')->shouldBeCalled()->willReturn(true);
$this->exists('filename')->shouldReturn(true);
}
/**
* @param \Gaufrette\Adapter $source
*/
function it_get_mtime_from_source($source)
{
$source->mtime('filename')->shouldBeCalled()->willReturn(1234);
$this->mtime('filename')->shouldReturn(1234);
}
/**
* @param \Gaufrette\Adapter $source
*/
function it_get_keys_from_source($source)
{
$source->keys()->willReturn(array('filename2', 'filename1', 'filename'));
$this->keys()->shouldReturn(array('filename', 'filename1', 'filename2'));
}
}
interface CacheTestExtendedAdapter extends \Gaufrette\Adapter,
\Gaufrette\Adapter\ChecksumCalculator,
\Gaufrette\Adapter\MetadataSupporter
{
}

View File

@@ -0,0 +1,208 @@
<?php
namespace spec\Gaufrette\Adapter;
//hack - mock php built-in functions
require_once 'functions.php';
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class DoctrineDbalSpec extends ObjectBehavior
{
/**
* @param \Doctrine\DBAL\Connection $connection
*/
function let($connection)
{
$this->beConstructedWith($connection, 'someTableName');
}
function it_is_adapter()
{
$this->shouldHaveType('Gaufrette\Adapter');
}
function it_is_checksum_calculator()
{
$this->shouldHaveType('Gaufrette\Adapter\ChecksumCalculator');
}
function it_does_not_handle_directories()
{
$this->isDirectory('filename')->shouldReturn(false);
}
/**
* @param \Doctrine\DBAL\Connection $connection
*/
function it_checks_if_file_exists($connection)
{
$connection
->quoteIdentifier(Argument::any())
->will(function ($argument) { return sprintf('"%s"', $argument[0]); });
$connection
->fetchColumn('SELECT COUNT("key") FROM "someTableName" WHERE "key" = :key', array('key' => 'filename'))
->willReturn(12);
$this->exists('filename')->shouldReturn(true);
$connection
->fetchColumn('SELECT COUNT("key") FROM "someTableName" WHERE "key" = :key', array('key' => 'filename'))
->willReturn(0);
$this->exists('filename')->shouldReturn(false);
}
/**
* @param \Doctrine\DBAL\Connection $connection
*/
function it_writes_to_new_file($connection)
{
$connection
->quoteIdentifier(Argument::any())
->will(function ($argument) { return sprintf('"%s"', $argument[0]); });
$connection
->fetchColumn('SELECT COUNT("key") FROM "someTableName" WHERE "key" = :key', array('key' => 'filename'))
->willReturn(false);
$connection
->insert(
'someTableName',
array(
'"content"' => 'some content',
'"mtime"' => strtotime('2012-10-10 23:10:10'),
'"checksum"' => '9893532233caff98cd083a116b013c0b',
'"key"' => 'filename'
))
->shouldBeCalled();
$this->write('filename', 'some content')->shouldReturn(12);
}
/**
* @param \Doctrine\DBAL\Connection $connection
*/
function it_write_file($connection)
{
$connection
->quoteIdentifier(Argument::any())
->will(function ($argument) { return sprintf('"%s"', $argument[0]); });
$connection
->fetchColumn('SELECT COUNT("key") FROM "someTableName" WHERE "key" = :key', array('key' => 'filename'))
->willReturn(true);
$connection
->update(
'someTableName',
array(
'"content"' => 'some content',
'"mtime"' => strtotime('2012-10-10 23:10:10'),
'"checksum"' => '9893532233caff98cd083a116b013c0b',
),
array(
'"key"' => 'filename'
))
->shouldBeCalled();
$this->write('filename', 'some content')->shouldReturn(12);
}
/**
* @param \Doctrine\DBAL\Connection $connection
*/
function it_reads_file($connection)
{
$connection
->quoteIdentifier(Argument::any())
->will(function ($argument) { return sprintf('"%s"', $argument[0]); });
$connection
->fetchColumn('SELECT "content" FROM "someTableName" WHERE "key" = :key', array('key' => 'filename'))
->willReturn('some content');
$this->read('filename')->shouldReturn('some content');
}
/**
* @param \Doctrine\DBAL\Connection $connection
*/
function it_calculates_checksum($connection)
{
$connection
->quoteIdentifier(Argument::any())
->will(function ($argument) { return sprintf('"%s"', $argument[0]); });
$connection
->fetchColumn('SELECT "checksum" FROM "someTableName" WHERE "key" = :key', array('key' => 'filename'))
->willReturn(1234);
$this->checksum('filename')->shouldReturn(1234);
}
/**
* @param \Doctrine\DBAL\Connection $connection
*/
function it_gets_mtime($connection)
{
$connection
->quoteIdentifier(Argument::any())
->will(function ($argument) { return sprintf('"%s"', $argument[0]); });
$connection
->fetchColumn('SELECT "mtime" FROM "someTableName" WHERE "key" = :key', array('key' => 'filename'))
->willReturn(1234);
$this->mtime('filename')->shouldReturn(1234);
}
/**
* @param \Doctrine\DBAL\Connection $connection
*/
function it_renames_file($connection)
{
$connection
->quoteIdentifier(Argument::any())
->will(function ($argument) { return sprintf('"%s"', $argument[0]); });
$connection
->update(
'someTableName',
array(
'"key"' => 'newFile',
),
array(
'"key"' => 'filename'
))
->shouldBeCalled()
->willReturn(1);
$this->rename('filename', 'newFile')->shouldReturn(true);
}
/**
* @param \Doctrine\DBAL\Connection $connection
* @param \Doctrine\DBAL\Statement $stmt
*/
function it_get_keys($connection, $stmt)
{
$stmt->fetchAll(\PDO::FETCH_COLUMN)->willReturn(array('filename', 'filename1', 'filename2'));
$connection
->quoteIdentifier(Argument::any())
->will(function ($argument) { return sprintf('"%s"', $argument[0]); });
$connection
->executeQuery('SELECT "key" FROM "someTableName"')
->willReturn($stmt);
$this->keys()->shouldReturn(array('filename', 'filename1', 'filename2'));
}
/**
* @param \Doctrine\DBAL\Connection $connection
*/
function it_deletes_file($connection)
{
$connection
->quoteIdentifier(Argument::any())
->will(function ($argument) { return sprintf('"%s"', $argument[0]); });
$connection
->delete('someTableName', array('"key"' => 'filename'))
->shouldBeCalled()
->willReturn(1);
$this->delete('filename')->shouldReturn(true);
}
}

View File

@@ -0,0 +1,288 @@
<?php
namespace spec\Gaufrette\Adapter;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class DropboxSpec extends ObjectBehavior
{
/**
* @param \Dropbox_API $dropbox
*/
function let($dropbox)
{
$this->beConstructedWith($dropbox);
}
function it_is_adapter()
{
$this->shouldHaveType('Gaufrette\Adapter');
}
/**
* @param \Dropbox_API $dropbox
*/
function it_reads_file($dropbox)
{
$dropbox->getFile('filename')->willReturn('some content');
$this->read('filename')->shouldReturn('some content');
}
/**
* @param \Dropbox_API $dropbox
*/
function it_does_not_mask_exception_from_client_during_read($dropbox)
{
$dropbox->getFile('filename')->willThrow(new \RuntimeException('read'));
$this->shouldThrow(new \RuntimeException('read'))->duringRead('filename');
}
/**
* @param \Dropbox_API $dropbox
*/
function it_does_not_read_file($dropbox)
{
$dropbox
->getFile('filename')
->willThrow(new \Dropbox_Exception_NotFound());
$this->read('filename')->shouldReturn(false);
}
/**
* @param \Dropbox_API $dropbox
*/
function it_checks_if_file_exists($dropbox)
{
$dropbox
->getMetaData('filename', false)
->willReturn(array(
"size" => "225.4KB",
"rev" => "35e97029684fe",
"thumb_exists" => false,
"bytes" => 230783,
"modified" => "Tue, 19 Jul 2011 21:55:38 +0000",
"client_mtime" => "Mon, 18 Jul 2011 18:04:35 +0000",
"path" => "/filename",
"is_dir" => false,
"icon" => "page_white_acrobat",
"root" => "dropbox",
"mime_type" => "application/pdf",
"revision" => 220823
));
$this->exists('filename')->shouldReturn(true);
$dropbox
->getMetaData('filename', false)
->willThrow(new \Dropbox_Exception_NotFound);
$this->exists('filename')->shouldReturn(false);
$dropbox
->getMetaData('filename', false)
->willReturn(array("is_deleted" => true));
$this->exists('filename')->shouldReturn(false);
}
/**
* @param \Dropbox_API $dropbox
*/
function it_does_not_mask_exception_from_client_during_check_if_exists($dropbox)
{
$dropbox
->getMetaData('filename', false)
->willThrow(new \RuntimeException('exists'));
$this->shouldThrow(new \RuntimeException('exists'))->duringExists('filename');
}
/**
* @param \Dropbox_API $dropbox
*/
function it_gets_keys($dropbox)
{
$dropbox
->getMetaData('/', true)
->willReturn(array(
'contents' => array(
array('path' => '/filename'),
array('path' => '/aaa/filename')
)
));
$this->keys()->shouldReturn(array('aaa', 'aaa/filename', 'filename'));
}
/**
* @param \Dropbox_API $dropbox
*/
function it_does_not_mask_exception_from_client_during_getting_keys($dropbox)
{
$dropbox
->getMetaData('/', true)
->willThrow(new \RuntimeException('keys'))
;
$this->shouldThrow(new \RuntimeException('keys'))->duringKeys();
}
/**
* @param \Dropbox_API $dropbox
*/
function it_checks_if_given_key_is_directory($dropbox)
{
$dropbox
->getMetaData('filename', false)
->willReturn(array(
"is_dir" => true
))
;
$this->isDirectory('filename')->shouldReturn(true);
$dropbox
->getMetaData('filename', false)
->willReturn(array(
"is_dir" => false
));
$this->isDirectory('filename')->shouldReturn(false);
}
/**
* @param \Dropbox_API $dropbox
*/
function it_writes_file($dropbox)
{
$dropbox
->putFile('filename', Argument::any())
->shouldBeCalled()
;
$this->write('filename', 'some content')->shouldReturn(12);
}
/**
* @param \Dropbox_API $dropbox
*/
function it_does_not_mask_exception_from_client_during_write($dropbox)
{
$dropbox
->putFile('filename', Argument::any())
->willThrow(new \RuntimeException('write'))
;
$this->shouldThrow(new \RuntimeException('write'))->duringWrite('filename', 'some content');
}
/**
* @param \Dropbox_API $dropbox
*/
function it_deletes_file($dropbox)
{
$dropbox
->delete('filename')
->shouldBeCalled()
;
$this->delete('filename')->shouldReturn(true);
}
/**
* @param \Dropbox_API $dropbox
*/
function it_does_not_delete_file($dropbox)
{
$dropbox
->delete('filename')
->willThrow(new \Dropbox_Exception_NotFound())
;
$this->delete('filename')->shouldReturn(false);
}
/**
* @param \Dropbox_API $dropbox
*/
function it_renames_file($dropbox)
{
$dropbox
->move('filename', 'filename2')
->shouldBeCalled()
;
$this->rename('filename', 'filename2')->shouldReturn(true);
}
/**
* @param \Dropbox_API $dropbox
*/
function it_does_not_rename_file($dropbox)
{
$dropbox
->move('filename', 'filename2')
->willThrow(new \Dropbox_Exception_NotFound())
;
$this->rename('filename', 'filename2')->shouldReturn(false);
}
/**
* @param \Dropbox_API $dropbox
*/
function it_fetches_mtime($dropbox)
{
$dropbox
->getMetaData('filename', false)
->willReturn(array(
"modified" => "Tue, 19 Jul 2011 21:55:38 +0000",
))
;
$this->mtime('filename')->shouldReturn(1311112538);
}
/**
* @param \Dropbox_API $dropbox
*/
function it_does_not_fetch_mtime_when_file_not_found($dropbox)
{
$dropbox
->getMetaData('filename', false)
->willThrow(new \Dropbox_Exception_NotFound())
;
$this->mtime('filename')->shouldReturn(false);
}
/**
* @param \Dropbox_API $dropbox
*/
function it_does_not_check_if_key_is_dir_when_file_not_found($dropbox)
{
$dropbox
->getMetaData('filename', false)
->willThrow(new \Dropbox_Exception_NotFound())
;
$this->isDirectory('filename')->shouldReturn(false);
}
/**
* @param \Dropbox_API $dropbox
*/
function it_fails_checking_if_key_is_dir_when_dropbox_throws_exception($dropbox)
{
$dropbox
->getMetaData('filename', false)
->willThrow(new \RuntimeException('some exception'))
;
$this->shouldThrow(new \RuntimeException('some exception'))->duringIsDirectory('filename');
}
}

View File

@@ -0,0 +1,107 @@
<?php
namespace spec\Gaufrette\Adapter;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use League\Flysystem\AdapterInterface;
use League\Flysystem\Config;
class FlysystemSpec extends ObjectBehavior
{
function let(AdapterInterface $adapter, Config $config)
{
$this->beConstructedWith($adapter, $config);
}
function it_is_adapter()
{
$this->shouldImplement('Gaufrette\Adapter');
}
function it_is_list_keys_aware()
{
$this->shouldImplement('Gaufrette\Adapter\ListKeysAware');
}
function it_reads_file(AdapterInterface $adapter)
{
$adapter->read('filename')->willReturn(['contents' => 'Hello.']);
$this->read('filename')->shouldReturn('Hello.');
}
function it_writes_file(AdapterInterface $adapter, Config $config)
{
$adapter->write('filename', 'Hello.', $config)->willReturn(array());
$this->write('filename', 'Hello.')->shouldReturn(array());
}
function it_checks_if_file_exists(AdapterInterface $adapter)
{
$adapter->has('filename')->willReturn(true);
$this->exists('filename')->shouldReturn(true);
}
function it_checks_if_file_exists_when_flysystem_returns_array(AdapterInterface $adapter)
{
$adapter->has('filename')->willReturn(['type' => 'file']);
$this->exists('filename')->shouldReturn(true);
}
function it_fetches_keys(AdapterInterface $adapter)
{
$adapter->listContents()->willReturn([[
'path' => 'folder',
'timestamp' => 1457104978,
'size' => 22,
'type' => 'dir',
]]);
$this->keys()->shouldReturn(['folder']);
}
function it_lists_keys(AdapterInterface $adapter)
{
$adapter->listContents()->willReturn([[
'path' => 'folder',
'timestamp' => 1457104978,
'size' => 22,
'type' => 'dir',
]]);
$this->listKeys()->shouldReturn([
'keys' => [],
'dirs' => ['folder'],
]);
}
function it_fetches_mtime(AdapterInterface $adapter)
{
$adapter->getTimestamp('filename')->willReturn(1457104978);
$this->mtime('filename')->shouldReturn(1457104978);
}
function it_deletes_file(AdapterInterface $adapter)
{
$adapter->delete('filename')->willReturn(true);
$this->delete('filename')->shouldReturn(true);
}
function it_renames_file(AdapterInterface $adapter)
{
$adapter->rename('oldfilename', 'newfilename')->willReturn(true);
$this->rename('oldfilename', 'newfilename')->shouldReturn(true);
}
function it_does_not_support_is_directory(AdapterInterface $adapter)
{
$this->shouldThrow('Gaufrette\Exception\UnsupportedAdapterMethodException')->duringisDirectory('folder');
}
}

View File

@@ -0,0 +1,228 @@
<?php
namespace spec\Gaufrette\Adapter;
//hack - mock php built-in functions
require_once 'functions.php';
use PhpSpec\ObjectBehavior;
class FtpSpec extends ObjectBehavior
{
function let()
{
$this->beConstructedWith('/home/l3l0', 'localhost');
}
function it_is_adapter()
{
$this->shouldHaveType('Gaufrette\Adapter');
}
function it_supports_native_list_keys()
{
$this->shouldHaveType('Gaufrette\Adapter\ListKeysAware');
}
function it_checks_if_file_exists_for_absolute_path()
{
$this->exists('filename')->shouldReturn(true);
$this->exists('aa/filename')->shouldReturn(false);
}
function it_checks_if_file_exists_for_relative_path()
{
$this->beConstructedWith('/home/l3l0/relative', 'localhost');
$this->exists('filename')->shouldReturn(true);
$this->exists('filename2')->shouldReturn(false);
$this->exists('aa/filename')->shouldReturn(false);
$this->exists('some/otherfilename')->shouldReturn(true);
}
function it_checks_if_dir_exists_for_symlink()
{
$this->exists('www')->shouldReturn(true);
$this->exists('vendor')->shouldReturn(true);
$this->exists('bbb')->shouldReturn(false);
}
function it_checks_if_dir_exists_with_special_and_unicode_chars_in_name()
{
$this->beConstructedWith('/home/l3l2', 'localhost');
$this->exists('a b c d -> žežulička')->shouldReturn(true);
}
function it_reads_file()
{
$this->read('filename')->shouldReturn('some content');
}
function it_does_not_read_file()
{
$this->read('filename2')->shouldReturn(false);
}
function it_writes_file()
{
$this->write('filename', 'some content')->shouldReturn(12);
}
function it_does_not_write_file()
{
$this->write('filename2', 'some content')->shouldReturn(false);
}
function it_renames_file()
{
$this->rename('filename', 'filename2')->shouldReturn(true);
}
function it_does_not_not_rename_file_when_target_file_is_invalid()
{
$this->rename('filename', 'invalid')->shouldReturn(false);
}
function it_fetches_keys_without_directories_dots()
{
$this->keys()->shouldReturn(array('filename', 'filename.exe', '.htaccess', 'aaa', 'aaa/filename'));
}
function it_fetches_keys_with_spaces_and_unicode_chars()
{
$this->beConstructedWith('/home/l3l2', 'localhost');
$this->keys()->shouldReturn(array('Žľuťoučký kůň.pdf', 'a b c d -> žežulička', 'a b c d -> žežulička/do re mi.pdf'));
}
function it_fetches_keys_recursive()
{
$this->beConstructedWith('/home/l3l3', 'localhost');
$this->keys()->shouldReturn(array('filename', 'filename.exe', '.htaccess', 'aaa', 'www', 'aaa/filename', 'www/filename', 'www/some', 'www/some/otherfilename'));
}
function it_lists_keys()
{
$this->listKeys()->shouldReturn(array(
'keys' => array('filename', 'filename.exe', '.htaccess', 'aaa/filename'),
'dirs' => array('aaa')
));
$this->listKeys('file')->shouldReturn(array(
'keys' => array('filename', 'filename.exe'),
'dirs' => array()
));
$this->listKeys('name')->shouldReturn(array(
'keys' => array(),
'dirs' => array()
));
$this->listKeys('aaa')->shouldReturn(array(
'keys' => array('aaa/filename'),
'dirs' => array('aaa')
));
$this->listKeys('aaa/')->shouldReturn(array(
'keys' => array('aaa/filename'),
'dirs' => array()
));
}
function it_fetches_mtime()
{
$this->mtime('filename')->shouldReturn(strtotime('2010-10-10 23:10:10'));
}
function it_throws_exception_when_mtime_is_not_supported_by_server()
{
$this->shouldThrow(new \RuntimeException('Server does not support ftp_mdtm function.'))->duringMtime('invalid');
}
function it_deletes_file()
{
$this->delete('filename')->shouldReturn(true);
}
function it_does_not_delete_file()
{
$this->delete('invalid')->shouldReturn(false);
}
/**
* @param \Gaufrette\Filesystem $filesystem
*/
function it_creates_file($filesystem)
{
$this->createFile('filename', $filesystem)->shouldReturnAnInstanceOf('\Gaufrette\File');
}
/**
* @param \Gaufrette\Filesystem $filesystem
*/
function it_creates_file_in_not_existing_directory($filesystem)
{
$this->createFile('bb/cc/dd/filename', $filesystem)->shouldReturnAnInstanceOf('\Gaufrette\File');
}
function it_checks_if_given_key_is_directory()
{
$this->isDirectory('aaa')->shouldReturn(true);
$this->isDirectory('filename')->shouldReturn(false);
}
function it_fetches_keys_with_hidden_files()
{
$this->beConstructedWith('/home/l3l1', 'localhost');
$this->keys()->shouldReturn(array('filename', '.htaccess'));
}
function it_checks_if_hidden_file_exists()
{
$this->beConstructedWith('/home/l3l1', 'localhost');
$this->exists('.htaccess')->shouldReturn(true);
}
function it_creates_base_directory_without_warning()
{
global $createdDirectory;
$createdDirectory = '';
$this->beConstructedWith('/home/l3l0/new', 'localhost', array('create' => true));
$this->listDirectory()->shouldReturn(array('keys' => array(), 'dirs' => array()));
}
function it_does_not_create_base_directory_and_should_throw_exception()
{
global $createdDirectory;
$createdDirectory = '';
$this->beConstructedWith('/home/l3l0/new', 'localhost', array('create' => false));
$this->shouldThrow(new \RuntimeException("The directory '/home/l3l0/new' does not exist."))->during('listDirectory', array());
}
function it_fetches_keys_for_windows()
{
$this->beConstructedWith('C:\Ftp', 'localhost');
$this->keys()->shouldReturn(array('archive', 'file1.zip', 'file2.zip'));
}
function it_supports_sizecalculator()
{
$this->shouldImplement('Gaufrette\Adapter\SizeCalculator');
$this->size('/path')->shouldReturn(5000);
}
function it_throws_an_exception_when_size_cant_be_fetched()
{
$this->shouldThrow('RuntimeException')->during('size', ['/erroneous']);
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace spec\Gaufrette\Adapter;
use PhpSpec\ObjectBehavior;
class GoogleCloudStorageSpec extends ObjectBehavior
{
public function let(\Google_Service_Storage $service)
{
$this->beConstructedWith($service, 'bucketName');
}
public function it_is_adapter()
{
$this->shouldHaveType('Gaufrette\Adapter');
}
public function it_supports_metadata()
{
$this->shouldHaveType('Gaufrette\Adapter\MetadataSupporter');
}
public function it_is_list_keys_aware()
{
$this->shouldHaveType('Gaufrette\Adapter\ListKeysAware');
}
}

View File

@@ -0,0 +1,168 @@
<?php
namespace spec\Gaufrette\Adapter;
use MongoDB\BSON\UTCDateTime;
use MongoDB\GridFS\Bucket;
use MongoDB\GridFS\Exception\FileNotFoundException;
use MongoDB\Model\BSONDocument;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class GridFSSpec extends ObjectBehavior
{
private $resources = [];
function let(Bucket $bucket)
{
$this->beConstructedWith($bucket);
}
function letGo()
{
array_map(function ($res) {
@fclose($res);
}, $this->resources);
}
function it_is_adapter()
{
$this->shouldHaveType('Gaufrette\Adapter');
}
function it_is_checksum_calculator()
{
$this->shouldHaveType('Gaufrette\Adapter\ChecksumCalculator');
}
function it_supports_metadata()
{
$this->shouldHaveType('Gaufrette\Adapter\MetadataSupporter');
}
function it_supports_native_list_keys()
{
$this->shouldHaveType('Gaufrette\Adapter\ListKeysAware');
}
function it_reads_file($bucket)
{
$this->resources[] = $readable = fopen('php://memory', 'rw');
fwrite($readable, 'some content');
fseek($readable, 0);
$bucket
->openDownloadStreamByName('filename')
->shouldBeCalled()
->willReturn($readable)
;
$this->read('filename')->shouldReturn('some content');
}
function it_does_not_fail_when_cannot_read($bucket)
{
$bucket->openDownloadStreamByName('filename')->willThrow(FileNotFoundException::class);
$this->read('filename')->shouldReturn(false);
}
function it_checks_if_file_exists($bucket, BSONDocument $file)
{
$bucket
->findOne(['filename' => 'filename'])
->willReturn($file)
;
$bucket
->findOne(['filename' => 'filename2'])
->willReturn(null)
;
$this->exists('filename')->shouldReturn(true);
$this->exists('filename2')->shouldReturn(false);
}
function it_deletes_file($bucket)
{
$bucket
->findOne(['filename' => 'filename'], ['projection' => ['_id' => 1]])
->willReturn($file = new BSONDocument(['_id' => 123]))
;
$bucket->delete(123)->shouldBeCalled();
$this->delete('filename')->shouldReturn(true);
}
function it_does_not_delete_file($bucket)
{
$bucket->findOne(['filename' => 'filename'], ['projection' => ['_id' => 1]])->willReturn(null);
$this->delete('filename')->shouldReturn(false);
}
function it_writes_file($bucket)
{
$this->resources[] = $writable = fopen('php://memory', 'rw');
$bucket
->openUploadStream('filename', ['metadata' => ['someother' => 'metadata']])
->willReturn($writable)
;
$this->setMetadata('filename', ['someother' => 'metadata']);
$this
->write('filename', 'some content')
->shouldReturn(12)
;
}
function it_renames_file($bucket)
{
$this->resources[] = $writable = fopen('php://memory', 'rw');
$this->resources[] = $readable = fopen('php://memory', 'rw');
fwrite($readable, 'some content');
fseek($readable, 0);
$bucket->openUploadStream('otherFilename', ['metadata' => ['some' => 'metadata']])->willReturn($writable);
$bucket->downloadToStreamByName('filename', $writable)->shouldBeCalled();
$bucket
->findOne(['filename' => 'filename'], ['projection' => ['_id' => 1]])
->willReturn($toDelete = new BSONDocument(['_id' => 1234]))
;
$bucket->delete(1234)->shouldBeCalled();
$this->setMetadata('filename', ['some' => 'metadata']);
$this->rename('filename', 'otherFilename')->shouldReturn(true);
}
function it_fetches_keys($bucket)
{
$bucket
->find([], ['projection' => ['filename' => 1]])
->willReturn([new BSONDocument(['filename' => 'filename']), new BSONDocument(['filename' => 'otherFilename'])])
;
$this->keys()->shouldReturn(['filename', 'otherFilename']);
}
function it_fetches_mtime($bucket)
{
$bucket
->findOne(['filename' => 'filename'], ['projection' => ['uploadDate' => 1]])
->willReturn(new BSONDocument(['uploadDate' => new UTCDateTime(12345000)]))
;
$this->mtime('filename')->shouldReturn(12345);
}
function it_calculates_checksum($bucket)
{
$bucket
->findOne(['filename' => 'filename'], ['projection' => ['md5' => 1]])
->willReturn(new BSONDocument(['md5' => 'md5123']))
;
$this->checksum('filename')->shouldReturn('md5123');
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace spec\Gaufrette\Adapter;
use PhpSpec\ObjectBehavior;
class InMemorySpec extends ObjectBehavior
{
function let()
{
$this->beConstructedWith(array(
'filename' => array('mtime' => 12345, 'content' => 'content'),
'filename2' => 'other content'
));
}
function it_is_adapter()
{
$this->shouldHaveType('Gaufrette\Adapter');
}
function it_is_a_mime_type_provider()
{
$this->shouldHaveType('Gaufrette\Adapter\MimeTypeProvider');
}
function it_gets_the_file_mime_type()
{
$this->mimeType('filename')->shouldReturn('text/plain');
}
function it_reads_file()
{
$this->read('filename')->shouldReturn('content');
}
function it_writes_file()
{
$this->write('filename', 'some content')->shouldReturn(12);
}
function it_renames_file()
{
$this->rename('filename', 'aaa/filename2')->shouldReturn(true);
$this->exists('filename')->shouldReturn(false);
$this->exists('aaa/filename2')->shouldReturn(true);
}
function it_checks_if_file_exists()
{
$this->exists('filename')->shouldReturn(true);
$this->exists('filenameTest')->shouldReturn(false);
}
function it_fetches_keys()
{
$this->keys()->shouldReturn(array('filename', 'filename2'));
}
function it_fetches_mtime()
{
$this->mtime('filename')->shouldReturn(12345);
}
function it_deletes_file()
{
$this->delete('filename')->shouldReturn(true);
$this->exists('filename')->shouldReturn(false);
}
function it_does_not_handle_dirs()
{
$this->isDirectory('filename')->shouldReturn(false);
$this->isDirectory('filename2')->shouldReturn(false);
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace spec\Gaufrette\Adapter;
use OpenCloud\ObjectStore\Exception\ObjectNotFoundException;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
/**
* LazyOpenCloudSpec
*
* @author Daniel Richter <nexyz9@gmail.com>
*/
class LazyOpenCloudSpec extends ObjectBehavior
{
/**
* @param \Gaufrette\Adapter\OpenStackCloudFiles\ObjectStoreFactoryInterface $objectStoreFactory
*/
function let($objectStoreFactory)
{
$this->beConstructedWith($objectStoreFactory, 'test-container-name');
}
function it_is_adapter()
{
$this->shouldHaveType('Gaufrette\Adapter');
}
/**
* @param \Gaufrette\Adapter\OpenStackCloudFiles\ObjectStoreFactoryInterface $objectStoreFactory
* @param \OpenCloud\ObjectStore\Service $objectStore
* @param \OpenCloud\ObjectStore\Resource\Container $container
*/
function it_initializes_object_store($objectStoreFactory, $objectStore, $container)
{
$objectStoreFactory->getObjectStore()->shouldBeCalled()->willReturn($objectStore);
$objectStore->getContainer("test-container-name")->shouldBeCalled()->willReturn($container);
$container->getObject("test-file-name")->willThrow(new ObjectNotFoundException());
$this->read("test-file-name");
}
}

View File

@@ -0,0 +1,161 @@
<?php
namespace spec\Gaufrette\Adapter;
use org\bovigo\vfs\vfsStream;
use PhpSpec\ObjectBehavior;
class LocalSpec extends ObjectBehavior
{
function let()
{
vfsStream::setup('test');
vfsStream::copyFromFileSystem(__DIR__.'/MockFilesystem');
$this->beConstructedWith(vfsStream::url('test'));
}
function it_is_adapter()
{
$this->shouldHaveType('Gaufrette\Adapter');
}
function it_is_checksum_calculator()
{
$this->shouldHaveType('Gaufrette\Adapter\ChecksumCalculator');
}
function it_is_a_mime_type_provider()
{
$this->shouldHaveType('Gaufrette\Adapter\MimeTypeProvider');
}
function it_gets_the_file_mime_type()
{
$this->mimeType('filename')->shouldReturn('text/plain');
}
function it_is_stream_factory()
{
$this->shouldHaveType('Gaufrette\Adapter\StreamFactory');
}
function it_reads_file()
{
$this->read('filename')->shouldReturn("content\n");
}
function it_writes_file()
{
$this->write('filename', 'some content')->shouldReturn(12);
}
function it_renames_file()
{
$this->rename('filename', 'aaa/filename2')->shouldReturn(true);
}
function it_checks_if_file_exists()
{
$this->exists('filename')->shouldReturn(true);
$this->exists('filename1')->shouldReturn(false);
}
function it_fetches_keys()
{
$expectedKeys = array('filename', 'dir', 'dir/file');
sort($expectedKeys);
$this->keys()->shouldReturn($expectedKeys);
}
function it_fetches_mtime()
{
$mtime = filemtime(vfsStream::url('test/filename'));
$this->mtime('filename')->shouldReturn($mtime);
}
function it_deletes_file()
{
$this->delete('filename')->shouldReturn(true);
$this->delete('filename1')->shouldReturn(false);
}
function it_checks_if_given_key_is_directory()
{
$this->isDirectory('dir')->shouldReturn(true);
$this->isDirectory('filename')->shouldReturn(false);
}
function it_creates_local_stream()
{
$this->createStream('filename')->shouldReturnAnInstanceOf('Gaufrette\Stream\Local');
}
function it_does_not_allow_to_read_path_above_main_file_directory()
{
$this
->shouldThrow(new \OutOfBoundsException(sprintf('The path "%s" is out of the filesystem.', vfsStream::url('filename'))))
->duringRead('../filename')
;
$this
->shouldThrow(new \OutOfBoundsException(sprintf('The path "%s" is out of the filesystem.', vfsStream::url('filename'))))
->duringExists('../filename')
;
}
function it_fails_when_directory_does_not_exists()
{
$this->beConstructedWith(vfsStream::url('other'));
$this
->shouldThrow(new \RuntimeException(sprintf('The directory "%s" does not exist.', vfsStream::url('other'))))
->duringRead('filename')
;
$this
->shouldThrow(new \RuntimeException(sprintf('The directory "%s" does not exist.', vfsStream::url('other'))))
->duringWrite('filename', 'some content')
;
$this
->shouldThrow(new \RuntimeException(sprintf('The directory "%s" does not exist.', vfsStream::url('other'))))
->duringRename('filename', 'otherFilename')
;
$this
->shouldThrow(new \RuntimeException(sprintf('The directory "%s" does not exist.', vfsStream::url('other'))))
->duringExists('filename')
;
$this
->shouldThrow(new \RuntimeException(sprintf('The directory "%s" does not exist.', vfsStream::url('other'))))
->duringKeys()
;
$this
->shouldThrow(new \RuntimeException(sprintf('The directory "%s" does not exist.', vfsStream::url('other'))))
->duringMtime('filename')
;
$this
->shouldThrow(new \RuntimeException(sprintf('The directory "%s" does not exist.', vfsStream::url('other'))))
->duringDelete('filename')
;
$this
->shouldThrow(new \RuntimeException(sprintf('The directory "%s" does not exist.', vfsStream::url('other'))))
->duringIsDirectory('filename')
;
$this
->shouldThrow(new \RuntimeException(sprintf('The directory "%s" does not exist.', vfsStream::url('other'))))
->duringCreateStream('filename')
;
$this
->shouldThrow(new \RuntimeException(sprintf('The directory "%s" does not exist.', vfsStream::url('other'))))
->duringChecksum('filename')
;
$this
->shouldThrow(new \RuntimeException(sprintf('The directory "%s" does not exist.', vfsStream::url('other'))))
->duringMimeType('filename')
;
}
function it_creates_directory_when_does_not_exists()
{
$this->beConstructedWith(vfsStream::url('test/other'), true);
$this->isDirectory('/')->shouldReturn(true);
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace spec\Gaufrette\Adapter;
use PhpSpec\ObjectBehavior;
class MogileFSSpec extends ObjectBehavior
{
function let()
{
$this->beConstructedWith('http://domain.com', array('localhost'));
}
function it_is_adapter()
{
$this->shouldHaveType('Gaufrette\Adapter');
}
function it_does_not_handle_mtime()
{
$this->mtime('filename')->shouldReturn(false);
$this->mtime('filename2')->shouldReturn(false);
}
}

View File

@@ -0,0 +1,255 @@
<?php
namespace spec\Gaufrette\Adapter;
use Guzzle\Http\Exception\BadResponseException;
use OpenCloud\Common\Exceptions\CreateUpdateError;
use OpenCloud\Common\Exceptions\DeleteError;
use OpenCloud\ObjectStore\Exception\ObjectNotFoundException;
use OpenCloud\ObjectStore\Resource\DataObject;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
/**
* OpenCloudSpec
*
* @author Chris Warner <cdw.lighting@gmail.com>
* @author Daniel Richter <nexyz9@gmail.com>
*/
class OpenCloudSpec extends ObjectBehavior
{
/**
* @param OpenCloud\ObjectStore\Service $objectStore
* @param OpenCloud\ObjectStore\Resource\Container $container
*/
function let($objectStore, $container)
{
$objectStore->getContainer("test")->willReturn($container);
$this->beConstructedWith($objectStore, 'test', false);
}
function it_is_adapter()
{
$this->shouldHaveType('Gaufrette\Adapter');
}
/**
* @param OpenCloud\ObjectStore\Resource\Container $container
* @param OpenCloud\ObjectStore\Resource\DataObject $object
*/
function it_reads_file($container, $object)
{
$object->getContent()->willReturn("Hello World");
$container->getObject("test")->willReturn($object);
$this->read('test')->shouldReturn('Hello World');
}
/**
* @param OpenCloud\ObjectStore\Resource\Container $container
*/
function it_reads_file_on_error_returns_false($container)
{
$container->getObject("test")->willThrow(new ObjectNotFoundException());
$this->read('test')->shouldReturn(false);
}
/**
* @param OpenCloud\ObjectStore\Resource\Container $container
* @param OpenCloud\ObjectStore\Resource\DataObject $object
*/
function it_writes_file_returns_size($container, $object)
{
$testData = "Hello World!";
$testDataSize = strlen($testData);
$object->getContentLength()->willReturn($testDataSize);
$container->uploadObject('test', $testData)->willReturn($object);
$this->write('test', $testData)->shouldReturn($testDataSize);
}
/**
* @param OpenCloud\ObjectStore\Resource\Container $container
*/
function it_writes_file_and_write_fails_returns_false($container)
{
$testData = "Hello World!";
$container->uploadObject('test', $testData)->willThrow(new CreateUpdateError());
$this->write('test', $testData)->shouldReturn(false);
}
/**
* @param OpenCloud\ObjectStore\Resource\Container $container
* @param OpenCloud\ObjectStore\Resource\DataObject $object
*/
function it_returns_true_if_key_exists($container, $object)
{
$container->getPartialObject('test')->willReturn($object);
$this->exists('test')->shouldReturn(true);
}
/**
* @param OpenCloud\ObjectStore\Resource\Container $container
*/
function it_returns_false_if_key_does_not_exist($container)
{
$container->getPartialObject('test')->willThrow(new BadResponseException());
$this->exists('test')->shouldReturn(false);
}
/**
* @param OpenCloud\ObjectStore\Resource\Container $container
* @param OpenCloud\ObjectStore\Resource\DataObject $object
*/
function it_deletes_file_on_success_returns_true($container, $object)
{
$object->delete()->willReturn(null);
$container->getObject("test")->willReturn($object);
$this->delete('test')->shouldReturn(true);
}
/**
* @param OpenCloud\ObjectStore\Resource\Container $container
* @param OpenCloud\ObjectStore\Resource\DataObject $object
*/
function it_deletes_file_returns_false_on_failure($container, $object)
{
$object->delete()->willThrow(new DeleteError());
$container->getObject("test")->willReturn($object);
$this->delete('test')->shouldReturn(false);
}
/**
* @param OpenCloud\ObjectStore\Resource\Container $container
*/
function it_deletes_file_if_file_does_not_exist_returns_false($container)
{
$container->getObject("test")->willThrow(new ObjectNotFoundException());
$this->delete('test')->shouldReturn(false);
}
/**
* @param OpenCloud\ObjectStore\Resource\Container $container
* @param OpenCloud\ObjectStore\Resource\DataObject $object
*/
function it_returns_checksum_if_file_exists($container, $object)
{
$object->getEtag()->willReturn("test String");
$container->getObject("test")->willReturn($object);
$this->checksum('test')->shouldReturn("test String");
}
/**
* @param OpenCloud\ObjectStore\Resource\Container $container
*/
function it_returns_false_when_file_does_not_exist($container)
{
$container->getObject("test")->willThrow(new ObjectNotFoundException());
$this->checksum('test')->shouldReturn(false);
}
/**
* @param OpenCloud\ObjectStore\Resource\Container $container
* @param OpenCloud\Common\Collection $objectList
* @param OpenCloud\ObjectStore\Resource\DataObject $object1
* @param OpenCloud\ObjectStore\Resource\DataObject $object2
* @param OpenCloud\ObjectStore\Resource\DataObject $object3
*/
function it_returns_files_as_sorted_array($container, $objectList, $object1, $object2, $object3)
{
$outputArray = array('key1', 'key2', 'key5');
$index = 0;
$object1->getName()->willReturn('key5');
$object2->getName()->willReturn('key2');
$object3->getName()->willReturn('key1');
$objects = array($object1, $object2, $object3);
$objectList->next()->will(
function () use ($objects, &$index) {
if ($index < count($objects)) {
$index++;
return $objects[$index - 1];
}
}
) ->shouldBeCalledTimes(count($objects) + 1);
$container->objectList()->willReturn($objectList);
$this->keys()->shouldReturn($outputArray);
}
/**
* @param OpenCloud\ObjectStore\Service $objectStore
*/
function it_throws_exception_if_container_does_not_exist($objectStore)
{
$containerName = 'container-does-not-exist';
$objectStore->getContainer($containerName)->willThrow(new BadResponseException());
$this->beConstructedWith($objectStore, $containerName);
$this->shouldThrow('\RuntimeException')->duringExists('test');
}
/**
* @param OpenCloud\ObjectStore\Service $objectStore
* @param OpenCloud\ObjectStore\Resource\Container $container
*/
function it_creates_container($objectStore, $container)
{
$containerName = 'container-does-not-yet-exist';
$filename = 'test';
$objectStore->getContainer($containerName)->willThrow(new BadResponseException());
$objectStore->createContainer($containerName)->willReturn($container);
$container->getPartialObject($filename)->willThrow(new BadResponseException());
$this->beConstructedWith($objectStore, $containerName, true);
$this->exists($filename)->shouldReturn(false);
}
/**
* @param OpenCloud\ObjectStore\Service $objectStore
*/
function it_throws_exeption_if_container_creation_fails($objectStore)
{
$containerName = 'container-does-not-yet-exist';
$objectStore->getContainer($containerName)->willThrow(new BadResponseException());
$objectStore->createContainer($containerName)->willReturn(false);
$this->beConstructedWith($objectStore, $containerName, true);
$this->shouldThrow('\RuntimeException')->duringExists('test');
}
function it_returns_false_if_the_object_does_not_exists_when_fetching_mtime($container)
{
$container->getObject('foo')->willThrow(ObjectNotFoundException::class);
$this->mtime('foo')->shouldReturn(false);
}
function it_fetches_file_mtime(DataObject $object, $container)
{
$container->getObject('foo')->willReturn($object);
$object->getLastModified()->willReturn('Tue, 13 Jun 2017 22:02:34 GMT');
$this->mtime('foo')->shouldReturn('1497391354');
}
}

View File

@@ -0,0 +1,156 @@
<?php
namespace spec\Gaufrette\Adapter;
if (!defined('NET_SFTP_TYPE_REGULAR')) {
define('NET_SFTP_TYPE_REGULAR', 1);
}
if (!defined('NET_SFTP_TYPE_DIRECTORY')) {
define('NET_SFTP_TYPE_DIRECTORY', 2);
}
use phpseclib\Net\SFTP as Base;
use PhpSpec\ObjectBehavior;
class PhpseclibSftpSpec extends ObjectBehavior
{
/**
* @param \spec\Gaufrette\Adapter\SFTP $sftp
*/
function let($sftp)
{
$this->beConstructedWith($sftp, '/home/l3l0', false, 'l3lo', 'password');
}
function it_is_adapter()
{
$this->shouldHaveType('Gaufrette\Adapter');
}
function it_is_file_factory()
{
$this->shouldHaveType('Gaufrette\Adapter\FileFactory');
}
function it_supports_native_list_keys()
{
$this->shouldHaveType('Gaufrette\Adapter\ListKeysAware');
}
/**
* @param \spec\Gaufrette\Adapter\SFTP $sftp
*/
function it_fetches_keys($sftp)
{
$sftp
->file_exists('/home/l3l0/')
->willReturn(true);
$sftp
->rawlist('/home/l3l0/')
->willReturn(array(
'filename' => array('type' => NET_SFTP_TYPE_REGULAR),
'filename1' => array('type' => NET_SFTP_TYPE_REGULAR),
'aaa' => array('type' => NET_SFTP_TYPE_DIRECTORY)
));
$sftp
->file_exists('/home/l3l0/aaa')
->willReturn(true);
$sftp
->rawlist('/home/l3l0/aaa')
->willReturn(array(
'filename' => array('type' => NET_SFTP_TYPE_REGULAR),
));
$this->keys()->shouldReturn(array('filename', 'filename1', 'aaa', 'aaa/filename'));
}
/**
* @param \spec\Gaufrette\Adapter\SFTP $sftp
*/
function it_reads_file($sftp)
{
$sftp->get('/home/l3l0/filename')->willReturn('some content');
$this->read('filename')->shouldReturn('some content');
}
/**
* @param \spec\Gaufrette\Adapter\SFTP $sftp
*/
function it_creates_and_writes_file($sftp)
{
$sftp->pwd()->willReturn('/home/l3l0');
$sftp->chdir('/home/l3l0')->willReturn(true);
$sftp->put('/home/l3l0/filename', 'some content')->willReturn(true);
$sftp->size('/home/l3l0/filename')->willReturn(12);
$this->write('filename', 'some content')->shouldReturn(12);
}
/**
* @param \spec\Gaufrette\Adapter\SFTP $sftp
*/
function it_renames_file($sftp)
{
$sftp->pwd()->willReturn('/home/l3l0');
$sftp->chdir('/home/l3l0')->willReturn(true);
$sftp
->rename('/home/l3l0/filename', '/home/l3l0/filename1')
->willReturn(true)
;
$this->rename('filename', 'filename1')->shouldReturn(true);
}
/**
* @param \spec\Gaufrette\Adapter\SFTP $sftp
*/
function it_should_check_if_file_exists($sftp)
{
$sftp->pwd()->willReturn('/home/l3l0');
$sftp->chdir('/home/l3l0')->willReturn(true);
$sftp->stat('/home/l3l0/filename')->willReturn(array(
'name' => '/home/l3l0/filename'
));
$sftp->stat('/home/l3l0/filename1')->willReturn(false);
$this->exists('filename')->shouldReturn(true);
$this->exists('filename1')->shouldReturn(false);
}
/**
* @param \spec\Gaufrette\Adapter\SFTP $sftp
*/
function it_should_check_is_directory($sftp)
{
$sftp->pwd()->willReturn('/home/l3l0');
$sftp->chdir('/home/l3l0')->willReturn(true);
$sftp->chdir('/home/l3l0/aaa')->willReturn(true);
$sftp->chdir('/home/l3l0/filename')->willReturn(false);
$this->isDirectory('aaa')->shouldReturn(true);
$this->isDirectory('filename')->shouldReturn(false);
}
/**
* @param \spec\Gaufrette\Adapter\SFTP $sftp
* @param \Gaufrette\Filesystem $filesystem
*/
function it_should_create_file($sftp, $filesystem)
{
$sftp->stat('/home/l3l0/filename')->willReturn(array(
'name' => '/home/l3l0/filename',
'size' => '30',
));
$this->createFile('filename', $filesystem)->beAnInstanceOf('Gaufrette\File');
}
}
class SFTP extends Base
{
public function __construct()
{
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace spec\Gaufrette\Adapter;
use org\bovigo\vfs\vfsStream;
use PhpSpec\ObjectBehavior;
class SafeLocalSpec extends ObjectBehavior
{
function let()
{
vfsStream::setup('test');
vfsStream::copyFromFileSystem(__DIR__.'/MockFilesystem');
$this->beConstructedWith(vfsStream::url('test'));
}
function it_is_local_adapter()
{
$this->shouldHaveType('Gaufrette\Adapter\Local');
}
function it_computes_path_using_base64()
{
rename(vfsStream::url('test/filename'), vfsStream::url('test/'.base64_encode('filename')));
$this->read('filename')->shouldReturn("content\n");
}
function it_computes_key_back_using_base64()
{
$this->keys()->shouldReturn(array(base64_decode('dir'), base64_decode('dir/file'), base64_decode('filename')));
}
}

View File

@@ -0,0 +1,127 @@
<?php
namespace spec\Gaufrette\Adapter;
//hack - mock php built-in functions
require_once 'functions.php';
use PhpSpec\ObjectBehavior;
/**
* @require Ssh\Sftp
*/
class SftpSpec extends ObjectBehavior
{
/**
* @param \Ssh\Sftp $sftp
*/
function let($sftp)
{
$this->beConstructedWith($sftp, '/home/l3l0');
}
function it_is_adapter()
{
$this->shouldHaveType('Gaufrette\Adapter');
}
function it_is_checksum_calculator()
{
$this->shouldHaveType('Gaufrette\Adapter\ChecksumCalculator');
}
/**
* @param \Ssh\Sftp $sftp
*/
function it_fetches_keys($sftp)
{
$sftp
->getUrl('/home/l3l0')
->willReturn('ssh+ssl://localhost/home/l3l0')
;
$sftp
->listDirectory('/home/l3l0', true)
->willReturn(array('files' => array('/home/l3l0/filename', '/home/l3l0/filename1', '/home/l3l0/aaa/filename')))
;
$this->keys()->shouldReturn(array('aaa', 'aaa/filename', 'filename', 'filename1'));
}
/**
* @param \Ssh\Sftp $sftp
*/
function it_reads_file($sftp)
{
$sftp
->getUrl('/home/l3l0')
->willReturn('ssh+ssl://localhost/home/l3l0')
;
$sftp
->read('/home/l3l0/filename')
->shouldBeCalled()
->willReturn('some content')
;
$this->read('filename')->shouldReturn('some content');
}
/**
* @param \Ssh\Sftp $sftp
*/
function it_writes_file($sftp)
{
$sftp
->getUrl('/home/l3l0')
->willReturn('ssh+ssl://localhost/home/l3l0')
;
$sftp
->write('/home/l3l0/filename', 'some content')
->shouldBeCalled()
->willReturn(12)
;
$this->write('filename', 'some content')->shouldReturn(12);
}
/**
* @param \Ssh\Sftp $sftp
*/
function it_renames_file($sftp)
{
$sftp
->getUrl('/home/l3l0')
->willReturn('ssh+ssl://localhost/home/l3l0')
;
$sftp
->rename('/home/l3l0/filename', '/home/l3l0/filename1')
->shouldBeCalled()
->willReturn(true)
;
$this->rename('filename', 'filename1')->shouldReturn(true);
}
/**
* @param \Ssh\Sftp $sftp
*/
function it_checks_if_file_exists($sftp)
{
$sftp
->getUrl('/home/l3l0')
->willReturn('ssh+ssl://localhost/home/l3l0')
;
$sftp
->getUrl('/home/l3l0/filename')
->shouldBeCalled()
->willReturn('ssh+ssl://localhost/home/l3l0/filename')
;
$sftp
->getUrl('/home/l3l0/filename1')
->shouldBeCalled()
->willReturn('ssh+ssl://localhost/home/l3l0/filename1')
;
$this->exists('filename')->shouldReturn(true);
$this->exists('filename1')->shouldReturn(false);
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace spec\Gaufrette\Adapter;
use PhpSpec\ObjectBehavior;
class ZipSpec extends ObjectBehavior
{
function let()
{
$this->beConstructedWith('somefile');
}
function it_is_adapter()
{
$this->shouldHaveType('Gaufrette\Adapter');
}
}

View File

@@ -0,0 +1,295 @@
<?php
namespace Gaufrette\Adapter;
global $createdDirectory;
function ftp_delete($connection, $path)
{
if ($path === '/home/l3l0/invalid') {
return false;
}
return true;
}
function ftp_mdtm($connection, $path)
{
if ($path === '/home/l3l0/invalid') {
return -1;
}
return \strtotime('2010-10-10 23:10:10');
}
function ftp_rename($connection, $from, $to)
{
return ! ('/home/l3l0/invalid' === $from or '/home/l3l0/invalid' === $to);
}
function ftp_fput($connection, $path, $fileResource, $mode)
{
if ('/home/l3l0/filename' === $path) {
return true;
}
return false;
}
function ftp_fget($connection, &$fileResource, $path, $mode)
{
if ('/home/l3l0/filename' === $path) {
$bytes = \fwrite($fileResource, 'some content');
return true;
}
return false;
}
function ftp_chdir($connection, $dirname)
{
if (in_array($dirname, array('/home/l3l0', '/home/l3l0/aaa', '/home/l3l0/relative', '/home/l3l0/relative/some', '/home/l3l1', '/home/l3l2', '/home/l3l2/a b c d -> žežulička', '/home/l3l3', 'C:\Ftp'))) {
return true;
}
global $createdDirectory;
if ($createdDirectory && $createdDirectory === $dirname) {
return true;
}
trigger_error(sprintf('%s: No such file or directory', $dirname), E_USER_WARNING);
return false;
}
function ftp_mkdir($connection, $dirname)
{
if (in_array($dirname, array('/home/l3l0/new'))) {
global $createdDirectory;
$createdDirectory = $dirname;
return true;
}
return false;
}
function ftp_connect($host, $port = 21, $timeout = 90)
{
if ('localhost' !== $host) {
return false;
}
return fopen('php://temp', 'r');
}
function ftp_close($connection)
{
return fclose($connection);
}
function ftp_rawlist($connection, $directory, $recursive = false)
{
$arguments = explode(' ', $directory, 2);
if ('/home/l3l0' === end($arguments))
{
return array(
"drwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 .",
"drwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 ..",
"drwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 aaa",
"-rwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 filename",
"-rwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 filename.exe",
"-rwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 .htaccess",
"lrwxrwxrwx 1 vincent vincent 11 Jul 12 12:16 www -> aaa",
"lrwxrwxrwx 1 vincent vincent 11 Jul 12 12:16 vendor -> bbb",
);
}
if ('/home/l3l0/aaa' === end($arguments))
{
return array(
"-rwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 filename",
);
}
if ('/home/l3l0/relative' === end($arguments))
{
return array(
"-rwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 filename",
"-rwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 some",
);
}
if ('/home/l3l0/relative/some' === end($arguments))
{
return array(
"-rwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 otherfilename",
);
}
if ('/home/l3l1' === end($arguments) && 0 === strpos(reset($arguments), '-al'))
{
return array(
"drwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 .",
"drwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 ..",
"-rwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 filename",
"-rwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 .htaccess",
);
}
if ('/home/l3l1' === end($arguments) && false === strpos(reset($arguments), '-al'))
{
return array(
"drwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 .",
"drwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 ..",
"-rwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 filename",
);
}
if ('/home/l3l2' === end($arguments))
{
return array(
"drwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 .",
"drwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 ..",
"drwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 a b c d -> žežulička",
"-rwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 Žľuťoučký kůň.pdf",
);
}
if ('/home/l3l2/a b c d -> žežulička' === end($arguments))
{
return array(
"drwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 .",
"drwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 ..",
"-rwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 do re mi.pdf",
);
}
if ('/home/l3l3' === end($arguments) && '-alR' === reset($arguments))
{
return array(
"/home/l3l3:",
"total: 12",
"drwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 .",
"drwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 ..",
"drwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 aaa",
"-rwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 filename",
"-rwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 filename.exe",
"-rwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 .htaccess",
"drwxrwxrwx 1 vincent vincent 11 Jul 12 12:16 www",
"lrwxrwxrwx 1 vincent vincent 11 Jul 12 12:16 vendor -> bbb",
"",
"/home/l3l3/aaa:",
"total: 8",
"-rwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 filename",
"",
"/home/l3l3/www:",
"drwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 .",
"drwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 ..",
"-rwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 filename",
"drwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 some",
"",
"/home/l3l3/www/some:",
"total 5",
"drwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 .",
"drwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 ..",
"-rwxr-x--- 15 vincent vincent 4096 Nov 3 21:31 otherfilename",
);
}
// https://github.com/KnpLabs/Gaufrette/issues/147
if ('C:\Ftp' === end($arguments))
{
return array(
"05-26-12 08:03PM <DIR> archive",
"12-04-12 06:57PM 16142 file1.zip",
"12-05-12 04:01PM 16142 file2.zip",
);
}
return array();
}
function ftp_login($connection, $username, $password)
{
if ('invalid' === $username) {
return false;
}
return true;
}
function time()
{
return \strtotime('2012-10-10 23:10:10');
}
function file_exists($path)
{
//fake it for ssh+ssl: protocol for SFTP testing, otherwise delegate to global
if (strpos($path, 'ssh+ssl:') === 0) {
return in_array($path, array('/home/l3l0/filename', '/home/somedir/filename', 'ssh+ssl://localhost/home/l3l0/filename')) ? true : false;
}
return \file_exists($path);
}
function extension_loaded($name)
{
global $extensionLoaded;
if (is_null($extensionLoaded)) {
return true;
}
return $extensionLoaded;
}
function opendir($url)
{
return true;
}
function apc_fetch($path)
{
return sprintf('%s content', $path);
}
function apc_store($path, $content, $ttl)
{
if ('prefix-apc-test/invalid' === $path) {
return false;
}
return sprintf('%s content', $path);
}
function apc_delete($path)
{
if ('prefix-apc-test/invalid' === $path) {
return false;
}
return true;
}
function apc_exists($path)
{
if ('prefix-apc-test/invalid' === $path) {
return false;
}
return true;
}
function ftp_size($connection, $path)
{
if ($path === '/erroneous') {
return -1;
}
return 5000;
}

View File

@@ -0,0 +1,186 @@
<?php
namespace spec\Gaufrette;
use PhpSpec\ObjectBehavior;
class FileSpec extends ObjectBehavior
{
/**
* @param \Gaufrette\Filesystem $filesystem
*/
function let($filesystem)
{
$this->beConstructedWith('filename', $filesystem);
}
function it_is_initializable()
{
$this->shouldHaveType('Gaufrette\File');
}
function it_gives_access_to_key()
{
$this->getKey()->shouldReturn('filename');
}
/**
* @param \Gaufrette\Filesystem $filesystem
*/
function it_gets_content($filesystem)
{
$filesystem->read('filename')->shouldBeCalled()->willReturn('Some content');
$this->getContent()->shouldReturn('Some content');
}
/**
* @param \Gaufrette\Filesystem $filesystem
*/
function it_gets_mtime($filesystem)
{
$filesystem->mtime('filename')->shouldBeCalled()->willReturn(1358797854);
$this->getMtime()->shouldReturn(1358797854);
}
/**
* @param \Gaufrette\Filesystem $filesystem
* @param \spec\Gaufrette\MetadataAdapter $adapter
*/
function it_pass_metadata_when_write_content($filesystem, $adapter)
{
$metadata = array('id' => '123');
$adapter->setMetadata('filename', $metadata)->shouldBeCalled();
$filesystem->write('filename', 'some content', true)->willReturn(12);
$filesystem->getAdapter()->willReturn($adapter);
$this->setContent('some content', $metadata);
}
/**
* @param \Gaufrette\Filesystem $filesystem
* @param \spec\Gaufrette\MetadataAdapter $adapter
*/
function it_pass_metadata_when_read_content($filesystem, $adapter)
{
$metadata = array('id' => '123');
$adapter->setMetadata('filename', $metadata)->shouldBeCalled();
$filesystem->read('filename')->willReturn('some content');
$filesystem->getAdapter()->willReturn($adapter);
$this->getContent($metadata);
}
/**
* @param \Gaufrette\Filesystem $filesystem
* @param \spec\Gaufrette\MetadataAdapter $adapter
*/
function it_pass_metadata_when_delete_content($filesystem, $adapter)
{
$metadata = array('id' => '123');
$adapter->setMetadata('filename', $metadata)->shouldBeCalled();
$filesystem->delete('filename')->willReturn(true);
$filesystem->getAdapter()->willReturn($adapter);
$this->delete($metadata);
}
/**
* @param \Gaufrette\Filesystem $filesystem
* @param \spec\Gaufrette\MetadataAdapter $adapter
*/
function it_sets_content_of_file($filesystem, $adapter)
{
$adapter->setMetadata('filename', array())->shouldNotBeCalled();
$filesystem->getAdapter()->willReturn($adapter);
$filesystem->write('filename', 'some content', true)->shouldBeCalled()->willReturn(21);
$this->setContent('some content')->shouldReturn(21);
$this->getContent('filename')->shouldReturn('some content');
}
function it_sets_key_as_name_by_default()
{
$this->getName()->shouldReturn('filename');
}
function it_sets_name()
{
$this->setName('name');
$this->getName()->shouldReturn('name');
}
/**
* @param \Gaufrette\Filesystem $filesystem
*/
function it_sets_size_for_new_file($filesystem)
{
$filesystem->write('filename', 'some content', true)->shouldBeCalled()->willReturn(21);
$this->setContent('some content');
$this->getSize()->shouldReturn(21);
}
/**
* @param \Gaufrette\Filesystem $filesystem
*/
function it_calculates_size_from_filesystem($filesystem)
{
$filesystem->size('filename')->shouldBeCalled()->willReturn(12);
$this->getSize()->shouldReturn(12);
}
/**
* @param \Gaufrette\Filesystem $filesystem
*/
function it_allows_to_set_size($filesystem)
{
$filesystem->read('filename')->shouldNotBeCalled();
$this->setSize(21);
$this->getSize()->shouldReturn(21);
}
/**
* @param \Gaufrette\Filesystem $filesystem
*/
function it_gets_zero_size_when_file_not_found($filesystem)
{
$filesystem->size('filename')->willThrow(new \Gaufrette\Exception\FileNotFound('filename'));
$this->getSize()->shouldReturn(0);
}
/**
* @param \Gaufrette\Filesystem $filesystem
*/
function it_check_if_file_with_key_exists_in_filesystem($filesystem)
{
$filesystem->has('filename')->willReturn(true);
$this->exists()->shouldReturn(true);
$filesystem->has('filename')->willReturn(false);
$this->exists()->shouldReturn(false);
}
/**
* @param \Gaufrette\Filesystem $filesystem
*/
function it_deletes_file_from_filesystem($filesystem)
{
$filesystem->delete('filename')->shouldBeCalled()->willReturn(true);
$this->delete()->shouldReturn(true);
}
function it_renames_file_from_filesystem($filesystem)
{
$filesystem->rename('filename', 'newname')->shouldBeCalled();
$this->rename('newname');
}
}
interface MetadataAdapter extends \Gaufrette\Adapter,
\Gaufrette\Adapter\MetadataSupporter
{}

View File

@@ -0,0 +1,73 @@
<?php
namespace spec\Gaufrette;
use PhpSpec\ObjectBehavior;
class FilesystemMapSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType('Gaufrette\FilesystemMap');
}
/**
* @param Gaufrette\Filesystem $filesystem
*/
function it_checks_if_has_mapped_filesystem($filesystem)
{
$this->set('some', $filesystem);
$this->has('some')->shouldReturn(true);
$this->has('other')->shouldReturn(false);
}
/**
* @param Gaufrette\Filesystem $filesystem
*/
function it_sets_mapped_filesystem($filesystem)
{
$this->set('some', $filesystem);
$this->get('some')->shouldReturn($filesystem);
}
function it_fails_when_get_filesystem_which_was_not_mapped()
{
$this
->shouldThrow(new \InvalidArgumentException('There is no filesystem defined for the "some" domain.'))
->duringGet('some')
;
}
/**
* @param Gaufrette\Filesystem $filesystem
*/
function it_removes_mapped_filesystem($filesystem)
{
$this->set('some', $filesystem);
$this->remove('some');
$this->has('some')->shouldReturn(false);
}
function it_fails_when_try_to_remove_filesystem_which_was_not_mapped()
{
$this
->shouldThrow(new \InvalidArgumentException('Cannot remove the "some" filesystem as it is not defined.'))
->duringRemove('some')
;
}
/**
* @param Gaufrette\Filesystem $filesystem
*/
function it_removes_all_filesystems($filesystem)
{
$this->set('some', $filesystem);
$this->set('other', $filesystem);
$this->clear();
$this->has('some')->shouldReturn(false);
$this->has('other')->shouldReturn(false);
$this->all()->shouldReturn(array());
}
}

View File

@@ -0,0 +1,432 @@
<?php
namespace spec\Gaufrette;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class FilesystemSpec extends ObjectBehavior
{
/**
* @param \Gaufrette\Adapter $adapter
*/
function let($adapter)
{
$this->beConstructedWith($adapter);
}
function it_is_initializable()
{
$this->shouldBeAnInstanceOf('Gaufrette\Filesystem');
$this->shouldBeAnInstanceOf('Gaufrette\FilesystemInterface');
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_gives_access_to_adapter($adapter)
{
$this->getAdapter()->shouldBe($adapter);
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_check_if_file_exists_using_adapter($adapter)
{
$adapter->exists('filename')->willReturn(true);
$adapter->exists('otherFilename')->willReturn(false);
$this->has('filename')->shouldReturn(true);
$this->has('otherFilename')->shouldReturn(false);
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_renames_file($adapter)
{
$adapter->exists('filename')->shouldBeCalled()->willReturn(true);
$adapter->exists('otherFilename')->shouldBeCalled()->willReturn(false);
$adapter->rename('filename', 'otherFilename')->shouldBeCalled()->willReturn(true);
$this->rename('filename', 'otherFilename')->shouldReturn(true);
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_fails_when_renamed_source_file_does_not_exist($adapter)
{
$adapter->exists('filename')->willReturn(false);
$this
->shouldThrow(new \Gaufrette\Exception\FileNotFound('filename'))
->duringRename('filename', 'otherFilename')
;
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_fails_when_renamed_target_file_exists($adapter)
{
$adapter->exists('filename')->willReturn(true);
$adapter->exists('otherFilename')->willReturn(true);
$this
->shouldThrow(new \Gaufrette\Exception\UnexpectedFile('otherFilename'))
->duringRename('filename', 'otherFilename')
;
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_fails_when_rename_is_not_successful($adapter)
{
$adapter->exists('filename')->willReturn(true);
$adapter->exists('otherFilename')->willReturn(false);
$adapter->rename('filename', 'otherFilename')->willReturn(false);
$this
->shouldThrow(new \RuntimeException('Could not rename the "filename" key to "otherFilename".'))
->duringRename('filename', 'otherFilename')
;
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_creates_file_object_for_key($adapter)
{
$adapter->exists('filename')->willReturn(true);
$this->get('filename')->shouldBeAnInstanceOf('Gaufrette\File');
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_does_not_get_file_object_when_file_with_key_does_not_exist($adapter)
{
$adapter->exists('filename')->willReturn(false);
$this
->shouldThrow(new \Gaufrette\Exception\FileNotFound('filename'))
->duringGet('filename')
;
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_gets_file_object_when_file_does_not_exist_but_can_be_created($adapter)
{
$adapter->exists('filename')->willReturn(false);
$this->get('filename', true)->shouldBeAnInstanceOf('Gaufrette\File');
}
/**
* @param \spec\Gaufrette\Adapter $extendedAdapter
* @param \Gaufrette\File $file
*/
function it_delegates_file_creation_to_adapter_when_adapter_is_file_factory($extendedAdapter, $file)
{
$this->beConstructedWith($extendedAdapter);
$extendedAdapter->exists('filename')->willReturn(true);
$extendedAdapter->createFile('filename', $this)->willReturn($file);
$this->get('filename')->shouldBe($file);
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_writes_content_to_new_file($adapter)
{
$adapter->exists('filename')->shouldBeCalled()->willReturn(false);
$adapter->write('filename', 'some content to write')->shouldBeCalled()->willReturn(21);
$this->write('filename', 'some content to write')->shouldReturn(21);
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_updates_content_of_file($adapter)
{
$adapter->write('filename', 'some content to write')->shouldBeCalled()->willReturn(21);
$this->write('filename', 'some content to write', true)->shouldReturn(21);
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_does_not_update_content_of_file_when_file_cannot_be_overwriten($adapter)
{
$adapter->exists('filename')->willReturn(true);
$adapter->write('filename', 'some content to write')->shouldNotBeCalled();
$this
->shouldThrow(new \Gaufrette\Exception\FileAlreadyExists('filename'))
->duringWrite('filename', 'some content to write')
;
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_fails_when_write_is_not_successful($adapter)
{
$adapter->exists('filename')->willReturn(false);
$adapter->write('filename', 'some content to write')->shouldBeCalled()->willReturn(false);
$this
->shouldThrow(new \RuntimeException('Could not write the "filename" key content.'))
->duringWrite('filename', 'some content to write')
;
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_read_file($adapter)
{
$adapter->exists('filename')->shouldBeCalled()->willReturn(true);
$adapter->read('filename')->shouldBeCalled()->willReturn('Some content');
$this->read('filename')->shouldReturn('Some content');
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_does_not_read_file_which_does_not_exist($adapter)
{
$adapter->exists('filename')->willReturn(false);
$this
->shouldThrow(new \Gaufrette\Exception\FileNotFound('filename'))
->duringRead('filename');
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_fails_when_read_is_not_successful($adapter)
{
$adapter->exists('filename')->willReturn(true);
$adapter->read('filename')->willReturn(false);
$this
->shouldThrow(new \RuntimeException('Could not read the "filename" key content.'))
->duringRead('filename')
;
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_deletes_file($adapter)
{
$adapter->exists('filename')->shouldBeCalled()->willReturn(true);
$adapter->delete('filename')->shouldBeCalled()->willReturn(true);
$this->delete('filename')->shouldReturn(true);
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_does_not_delete_file_which_does_not_exist($adapter)
{
$adapter->exists('filename')->willReturn(false);
$this
->shouldThrow(new \Gaufrette\Exception\FileNotFound('filename'))
->duringDelete('filename')
;
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_fails_when_delete_is_not_successful($adapter)
{
$adapter->exists('filename')->willReturn(true);
$adapter->delete('filename')->willReturn(false);
$this
->shouldThrow(new \RuntimeException('Could not remove the "filename" key.'))
->duringDelete('filename')
;
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_should_get_all_keys($adapter)
{
$keys = array('filename', 'filename1', 'filename2');
$adapter->keys()->willReturn($keys);
$this->keys()->shouldReturn($keys);
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_match_listed_keys_using_specified_pattern($adapter)
{
$keys = array('filename', 'filename1', 'filename2', 'testKey', 'KeyTest', 'testkey');
$adapter->keys()->willReturn($keys);
$adapter->isDirectory(Argument::any())->willReturn(false);
$this->listKeys()->shouldReturn(
array(
'keys' => array('filename', 'filename1', 'filename2', 'testKey', 'KeyTest', 'testkey'),
'dirs' => array()
)
);
$this->listKeys('filename')->shouldReturn(
array(
'keys' => array('filename', 'filename1', 'filename2'),
'dirs' => array()
)
);
$this->listKeys('Key')->shouldReturn(
array(
'keys' => array('KeyTest'),
'dirs' => array()
)
);
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_listing_directories_using_adapter_is_directory_method($adapter)
{
$keys = array('filename', 'filename1', 'filename2', 'testKey', 'KeyTest', 'testkey');
$adapter->keys()->willReturn($keys);
$adapter->isDirectory('filename')->willReturn(false);
$adapter->isDirectory('filename2')->willReturn(false);
$adapter->isDirectory('KeyTest')->willReturn(false);
$adapter->isDirectory('testkey')->willReturn(false);
$adapter->isDirectory('filename1')->willReturn(true);
$adapter->isDirectory('testKey')->willReturn(true);
$this->listKeys()->shouldReturn(
array(
'keys' => array('filename', 'filename2', 'KeyTest', 'testkey'),
'dirs' => array('filename1', 'testKey')
)
);
$this->listKeys('filename')->shouldReturn(
array(
'keys' => array('filename', 'filename2'),
'dirs' => array('filename1')
)
);
$this->listKeys('Key')->shouldReturn(
array(
'keys' => array('KeyTest'),
'dirs' => array()
)
);
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_gets_mtime_of_file_using_adapter($adapter)
{
$adapter->exists('filename')->willReturn(true);
$adapter->mtime('filename')->willReturn(1234567);
$this->mtime('filename')->shouldReturn(1234567);
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_does_not_get_mtime_of_file_which_does_not_exist($adapter)
{
$adapter->exists('filename')->willReturn(false);
$this
->shouldThrow(new \Gaufrette\Exception\FileNotFound('filename'))
->duringMtime('filename')
;
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_calculates_file_checksum($adapter)
{
$adapter->exists('filename')->shouldBeCalled()->willReturn(true);
$adapter->read('filename')->willReturn('some content');
$this->checksum('filename')->shouldReturn(md5('some content'));
}
/**
* @param \Gaufrette\Adapter $adapter
*/
function it_does_not_calculate_checksum_of_file_which_does_not_exist($adapter)
{
$adapter->exists('filename')->shouldBeCalled()->willReturn(false);
$this
->shouldThrow(new \Gaufrette\Exception\FileNotFound('filename'))
->duringChecksum('filename');
}
/**
* @param \spec\Gaufrette\Adapter $extendedAdapter
*/
function it_delegates_checksum_calculation_to_adapter_when_adapter_is_checksum_calculator($extendedAdapter)
{
$this->beConstructedWith($extendedAdapter);
$extendedAdapter->exists('filename')->shouldBeCalled()->willReturn(true);
$extendedAdapter->read('filename')->shouldNotBeCalled();
$extendedAdapter->checksum('filename')->shouldBeCalled()->willReturn(12);
$this->checksum('filename')->shouldReturn(12);
}
/**
* @param \spec\Gaufrette\Adapter $extendedAdapter
*/
function it_delegates_mime_type_resolution_to_adapter_when_adapter_is_mime_type_provider($extendedAdapter)
{
$this->beConstructedWith($extendedAdapter);
$extendedAdapter->exists('filename')->willReturn(true);
$extendedAdapter->mimeType('filename')->willReturn('text/plain');
$this->mimeType('filename')->shouldReturn('text/plain');
}
function it_cannot_resolve_mime_type_if_the_adapter_cannot_provide_it($adapter)
{
$adapter->exists('filename')->willReturn(true);
$this
->shouldThrow(new \LogicException(sprintf('Adapter "%s" cannot provide MIME type', get_class($adapter->getWrappedObject()))))
->duringMimeType('filename');
}
}
interface Adapter extends \Gaufrette\Adapter,
\Gaufrette\Adapter\FileFactory,
\Gaufrette\Adapter\StreamFactory,
\Gaufrette\Adapter\ChecksumCalculator,
\Gaufrette\Adapter\MetadataSupporter,
\Gaufrette\Adapter\MimeTypeProvider
{}

View File

@@ -0,0 +1,29 @@
<?php
namespace spec\Gaufrette\Stream;
use PhpSpec\ObjectBehavior;
use Gaufrette\StreamMode;
use org\bovigo\vfs\vfsStream;
class LocalSpec extends ObjectBehavior
{
function it_throws_runtime_exception_when_file_doesnt_exists()
{
$this->beConstructedWith(vfsStream::url('other'));
$this->shouldThrow('\RuntimeException')->duringOpen(new StreamMode('r'));
}
function it_throws_runtime_exception_when_file_doesnt_exists_and_custom_error_handler_specified()
{
$custom_error_handler = function ($errno, $errstr, $errfile, $errline) {
throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
};
set_error_handler($custom_error_handler);
$this->beConstructedWith(vfsStream::url('other'));
$this->shouldThrow('\RuntimeException')->duringOpen(new StreamMode('r'));
restore_error_handler();
}
}

View File

@@ -0,0 +1,142 @@
<?php
namespace spec\Gaufrette;
use PhpSpec\ObjectBehavior;
class StreamModeSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->beConstructedWith('r');
$this->shouldHaveType('Gaufrette\StreamMode');
}
function it_gives_access_to_mode()
{
$this->beConstructedWith('r+');
$this->getMode()->shouldReturn('r+');
}
function it_allows_write_only()
{
$this->beConstructedWith('w');
$this->allowsWrite()->shouldReturn(true);
$this->allowsRead()->shouldReturn(false);
}
function it_allows_write_and_read()
{
$this->beConstructedWith('w+');
$this->allowsWrite()->shouldReturn(true);
$this->allowsRead()->shouldReturn(true);
}
function it_allows_read_only()
{
$this->beConstructedWith('r');
$this->allowsWrite()->shouldReturn(false);
$this->allowsRead()->shouldReturn(true);
}
function it_allows_to_existing_file_opening()
{
$this->beConstructedWith('r');
$this->allowsExistingFileOpening()->shouldReturn(true);
}
function it_does_not_allow_to_existing_file_opening()
{
$this->beConstructedWith('x');
$this->allowsExistingFileOpening()->shouldReturn(false);
}
function it_allows_new_file_opening()
{
$this->beConstructedWith('w');
$this->allowsNewFileOpening()->shouldReturn(true);
}
function it_does_not_allow_new_file_opening()
{
$this->beConstructedWith('r');
$this->allowsNewFileOpening()->shouldReturn(false);
}
function it_implies_existing_content_deletion()
{
$this->beConstructedWith('w+');
$this->allowsNewFileOpening()->shouldReturn(true);
}
function it_does_not_implies_existing_content_deletion()
{
$this->beConstructedWith('r+');
$this->allowsNewFileOpening()->shouldReturn(false);
}
function it_implies_positioning_cursor_at_the_beginning()
{
$this->beConstructedWith('r+');
$this->impliesPositioningCursorAtTheBeginning()->shouldReturn(true);
}
function it_does_no_implies_positioning_cursor_at_the_beginning()
{
$this->beConstructedWith('a');
$this->impliesPositioningCursorAtTheBeginning()->shouldReturn(false);
}
function it_implies_positioning_cursor_at_the_end()
{
$this->beConstructedWith('a');
$this->impliesPositioningCursorAtTheEnd()->shouldReturn(true);
}
function it_does_no_implies_positioning_cursor_at_the_end()
{
$this->beConstructedWith('w');
$this->impliesPositioningCursorAtTheEnd()->shouldReturn(false);
}
function it_should_be_binary()
{
$this->beConstructedWith('wb+');
$this->isBinary()->shouldReturn(true);
}
function it_should_not_be_binary()
{
$this->beConstructedWith('w+');
$this->isBinary()->shouldReturn(false);
}
function it_should_not_be_text()
{
$this->beConstructedWith('wb+');
$this->isText()->shouldReturn(false);
}
function it_should_be_text()
{
$this->beConstructedWith('w+');
$this->isText()->shouldReturn(true);
}
}

View File

@@ -0,0 +1,295 @@
<?php
namespace spec\Gaufrette;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class StreamWrapperSpec extends ObjectBehavior
{
/**
* @param \Gaufrette\FilesystemMap $map
* @param \Gaufrette\Filesystem $filesystem
* @param \Gaufrette\Stream $stream
*/
function let($map, $filesystem, $stream)
{
$filesystem->createStream('filename')->willReturn($stream);
$map->get('some')->willReturn($filesystem);
$this->setFilesystemMap($map);
}
function it_is_initializable()
{
$this->shouldHaveType('Gaufrette\StreamWrapper');
}
/**
* @param \Gaufrette\Stream $stream
*/
function it_opens_stream($stream)
{
$stream->open(Argument::any())->willReturn(true);
$this->stream_open('gaufrette://some/filename', 'r+')->shouldReturn(true);
}
function it_does_not_open_stream_when_key_is_not_defined()
{
$this
->shouldThrow(new \InvalidArgumentException('The specified path (gaufrette://some) is invalid.'))
->duringStream_open('gaufrette://some', 'r+');
}
function it_does_not_open_stream_when_host_is_not_defined()
{
$this
->shouldThrow(new \InvalidArgumentException('The specified path (gaufrette:///somefile) is invalid.'))
->duringStream_open('gaufrette:///somefile', 'r+')
;
}
function it_does_not_read_from_stream_when_is_not_opened()
{
$this->stream_read(10)->shouldReturn(false);
}
/**
* @param \Gaufrette\Stream $stream
*/
function it_does_not_read_from_stream($stream)
{
$stream->open(Argument::any())->willReturn(true);
$stream->read(4)->willReturn('some');
$this->stream_open('gaufrette://some/filename', 'r+');
$this->stream_read(4)->shouldReturn('some');
}
function it_does_not_write_to_stream_when_is_not_opened()
{
$this->stream_write('some content')->shouldReturn(0);
}
/**
* @param \Gaufrette\Stream $stream
*/
function it_writes_to_stream($stream)
{
$stream->open(Argument::any())->willReturn(true);
$stream->write('some content')->shouldBeCalled()->willReturn(12);
$this->stream_open('gaufrette://some/filename', 'w+');
$this->stream_write('some content')->shouldReturn(12);
}
/**
* @param \Gaufrette\Stream $stream
*/
function it_does_not_close_stream_when_is_not_opened($stream)
{
$stream->close()->shouldNotBeCalled();
$this->stream_close();
}
/**
* @param \Gaufrette\Stream $stream
*/
function it_closes_stream($stream)
{
$stream->open(Argument::any())->willReturn(true);
$stream->close()->shouldBeCalled();
$this->stream_open('gaufrette://some/filename', 'w+');
$this->stream_close();
}
/**
* @param \Gaufrette\Stream $stream
*/
function it_does_not_flush_stream_when_is_not_opened($stream)
{
$stream->flush()->shouldNotBeCalled();
$this->stream_flush();
}
/**
* @param \Gaufrette\Stream $stream
*/
function it_flushes_stream($stream)
{
$stream->open(Argument::any())->willReturn(true);
$stream->flush()->shouldBeCalled();
$this->stream_open('gaufrette://some/filename', 'w+');
$this->stream_flush();
}
/**
* @param \Gaufrette\Stream $stream
*/
function it_does_not_seek_in_stream_when_is_not_opened($stream)
{
$stream->seek(12, SEEK_SET)->shouldNotBeCalled();
$this->stream_seek(12, SEEK_SET);
}
/**
* @param \Gaufrette\Stream $stream
*/
function it_seeks_in_stream($stream)
{
$stream->open(Argument::any())->willReturn(true);
$stream->seek(12, SEEK_SET)->shouldBeCalled()->willReturn(true);
$this->stream_open('gaufrette://some/filename', 'w+');
$this->stream_seek(12, SEEK_SET)->shouldReturn(true);
}
/**
* @param \Gaufrette\Stream $stream
*/
function it_does_not_tell_about_position_in_stream_when_is_not_opened($stream)
{
$stream->tell()->shouldNotBeCalled();
$this->stream_tell();
}
/**
* @param \Gaufrette\Stream $stream
*/
function it_does_tell_about_position_in_stream($stream)
{
$stream->open(Argument::any())->willReturn(true);
$stream->tell()->shouldBeCalled()->willReturn(12);
$this->stream_open('gaufrette://some/filename', 'w+');
$this->stream_tell()->shouldReturn(12);
}
/**
* @param \Gaufrette\Stream $stream
*/
function it_does_not_mark_as_eof_if_stream_is_not_opened($stream)
{
$stream->eof()->shouldNotBeCalled();
$this->stream_eof();
}
/**
* @param \Gaufrette\Stream $stream
*/
function it_checks_if_eof($stream)
{
$stream->open(Argument::any())->willReturn(true);
$this->stream_open('gaufrette://some/filename', 'w+');
$stream->eof()->willReturn(false);
$this->stream_eof()->shouldReturn(false);
$stream->eof()->willReturn(true);
$this->stream_eof()->shouldReturn(true);
}
function it_does_not_get_stat_when_is_not_open()
{
$this->stream_stat()->shouldReturn(false);
}
/**
* @param \Gaufrette\Stream $stream
*/
function it_stats_file($stream)
{
$stat = array(
'dev' => 1,
'ino' => 12,
'mode' => 0777,
'nlink' => 0,
'uid' => 123,
'gid' => 1,
'rdev' => 0,
'size' => 666,
'atime' => 1348030800,
'mtime' => 1348030800,
'ctime' => 1348030800,
'blksize' => 5,
'blocks' => 1,
);
$stream->open(Argument::any())->willReturn(true);
$stream->stat()->willReturn($stat);
$this->stream_open('gaufrette://some/filename', 'w+');
$this->stream_stat()->shouldReturn($stat);
}
/**
* @param \Gaufrette\Stream $stream
*/
function it_should_stat_from_url($stream)
{
$stat = array(
'dev' => 1,
'ino' => 12,
'mode' => 0777,
'nlink' => 0,
'uid' => 123,
'gid' => 1,
'rdev' => 0,
'size' => 666,
'atime' => 1348030800,
'mtime' => 1348030800,
'ctime' => 1348030800,
'blksize' => 5,
'blocks' => 1,
);
$stream->open(Argument::any())->willReturn(true);
$stream->stat()->willReturn($stat);
$this->url_stat('gaufrette://some/filename', STREAM_URL_STAT_LINK)->shouldReturn($stat);
}
/**
* @param \Gaufrette\Stream $stream
*/
function it_stats_even_if_it_cannot_be_open($filesystem, $stream)
{
$filesystem->createStream('dir/')->willReturn($stream);
$stream->open(Argument::any())->willThrow(new \RuntimeException);
$stream->stat(Argument::any())->willReturn(array('mode' => 16893));
$this->url_stat('gaufrette://some/dir/', STREAM_URL_STAT_LINK)->shouldReturn(array('mode' => 16893));
}
/**
* @param \Gaufrette\Stream $stream
*/
function it_does_not_unlink_when_cannot_open($stream)
{
$stream->open(Argument::any())->willThrow(new \RuntimeException);
$this->unlink('gaufrette://some/filename')->shouldReturn(false);
}
/**
* @param \Gaufrette\Stream $stream
*/
function it_unlinks_file($stream)
{
$stream->open(Argument::any())->willReturn(true);
$stream->unlink()->willReturn(true);
$this->unlink('gaufrette://some/filename')->shouldReturn(true);
}
function it_does_not_cast_stream_if_is_not_opened()
{
$this->stream_cast(STREAM_CAST_FOR_SELECT)->shouldReturn(false);
}
/**
* @param \Gaufrette\Stream $stream
*/
function it_casts_stream($stream)
{
$stream->open(Argument::any())->willReturn(true);
$stream->cast(STREAM_CAST_FOR_SELECT)->willReturn('resource');
$this->stream_open('gaufrette://some/filename', 'w+');
$this->stream_cast(STREAM_CAST_FOR_SELECT)->shouldReturn('resource');
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace spec\Gaufrette\Util;
use PHPSpec\ObjectBehavior;
class ChecksumSpec extends ObjectBehavior
{
function let()
{
$path = __DIR__.DIRECTORY_SEPARATOR.'testFile';
file_put_contents($path, 'some other content');
}
function it_calculates_checksum_from_content()
{
$this->fromContent('some content')->shouldReturn(md5('some content'));
}
function it_calculates_checksum_from_filepath()
{
$path = __DIR__.DIRECTORY_SEPARATOR.'testFile';
$this->fromFile($path)->shouldReturn(md5('some other content'));
}
function letgo()
{
$path = __DIR__.DIRECTORY_SEPARATOR.'testFile';
@unlink(__DIR__.DIRECTORY_SEPARATOR.'testFile');
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace spec\Gaufrette\Util;
use PhpSpec\ObjectBehavior;
class PathSpec extends ObjectBehavior
{
function it_checks_if_path_is_absolute()
{
$this->isAbsolute('/home/path')->shouldBe(true);
$this->isAbsolute('home/path')->shouldBe(false);
$this->isAbsolute('../home/path')->shouldBe(false);
$this->isAbsolute('protocol://home/path')->shouldBe(true);
}
function it_normalizes_file_path()
{
$this->normalize('C:\\some\other.txt')->shouldReturn('c:/some/other.txt');
$this->normalize('..\other.txt')->shouldReturn('../other.txt');
$this->normalize('..\other.txt')->shouldReturn('../other.txt');
$this->normalize('/home/other/../new')->shouldReturn('/home/new');
$this->normalize('/home/other/./new')->shouldReturn('/home/other/new');
$this->normalize('protocol://home/other.txt')->shouldReturn('protocol://home/other.txt');
}
function it_returns_unix_style_dirname()
{
$this->dirname('a/test/path')->shouldReturn('a/test');
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace spec\Gaufrette\Util;
use PhpSpec\ObjectBehavior;
class SizeSpec extends ObjectBehavior
{
function it_calculates_size_of_content()
{
$this->fromContent('some content')->shouldReturn(12);
$this->fromContent('some other content')->shouldReturn(18);
$this->fromContent('some')->shouldReturn(4);
}
}

View File

@@ -0,0 +1,84 @@
<?php
namespace Gaufrette;
/**
* Interface for the filesystem adapters.
*
* @author Antoine Hérault <antoine.herault@gmail.com>
* @author Leszek Prabucki <leszek.prabucki@gmail.com>
*/
interface Adapter
{
/**
* Reads the content of the file.
*
* @param string $key
*
* @return string|bool if cannot read content
*/
public function read($key);
/**
* Writes the given content into the file.
*
* @param string $key
* @param string $content
*
* @return int|bool The number of bytes that were written into the file
*/
public function write($key, $content);
/**
* Indicates whether the file exists.
*
* @param string $key
*
* @return bool
*/
public function exists($key);
/**
* Returns an array of all keys (files and directories).
*
* @return array
*/
public function keys();
/**
* Returns the last modified time.
*
* @param string $key
*
* @return int|bool An UNIX like timestamp or false
*/
public function mtime($key);
/**
* Deletes the file.
*
* @param string $key
*
* @return bool
*/
public function delete($key);
/**
* Renames a file.
*
* @param string $sourceKey
* @param string $targetKey
*
* @return bool
*/
public function rename($sourceKey, $targetKey);
/**
* Check if key is directory.
*
* @param string $key
*
* @return bool
*/
public function isDirectory($key);
}

View File

@@ -0,0 +1,196 @@
<?php
namespace Gaufrette\Adapter;
use AmazonS3 as AmazonClient;
use Gaufrette\Adapter;
@trigger_error('The '.__NAMESPACE__.'\AclAwareAmazonS3 adapter is deprecated since version 0.4 and will be removed in 1.0. Use the AwsS3 adapter instead.', E_USER_DEPRECATED);
/**
* Makes the AmazonS3 adapter ACL aware. Uses the AWS SDK for PHP v1.x.
*
* See the AwsS3 adapter for using the AWS SDK for PHP v2.x. There is
* no distinction in the AwsS3 adapter between an ACL aware adapter
* and regular adapter.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*
* @deprecated The AclAwareAmazonS3 adapter is deprecated since version 0.4 and will be removed in 1.0. Use the AwsS3 adapter instead.
*/
class AclAwareAmazonS3 implements Adapter,
MetadataSupporter
{
protected $delegate;
protected $s3;
protected $bucketName;
protected $aclConstant = AmazonClient::ACL_PRIVATE;
protected $users = array();
public function __construct(Adapter $delegate, AmazonClient $s3, $bucketName)
{
$this->delegate = $delegate;
$this->s3 = $s3;
$this->bucketName = $bucketName;
}
public function setAclConstant($constant)
{
if (!defined($constant = 'AmazonS3::ACL_'.strtoupper($constant))) {
throw new \InvalidArgumentException(sprintf('The ACL constant "%s" does not exist on AmazonS3.', $constant));
}
$this->aclConstant = constant($constant);
}
public function setUsers(array $users)
{
$this->users = array();
foreach ($users as $user) {
if (!isset($user['permission'])) {
throw new \InvalidArgumentException(sprintf('setUsers() expects an array where each item contains a "permission" key, but got %s.', json_encode($user)));
}
if (!defined($constant = 'AmazonS3::GRANT_'.strtoupper($user['permission']))) {
throw new \InvalidArgumentException('The permission must be the suffix for one of the AmazonS3::GRANT_ constants.');
}
$user['permission'] = constant($constant);
if (isset($user['group'])) {
if (!defined($constant = 'AmazonS3::USERS_'.strtoupper($user['group']))) {
throw new \InvalidArgumentException('The group must be the suffix for one of the AmazonS3::USERS_ constants.');
}
$user['id'] = constant($constant);
unset($user['group']);
} elseif (!isset($user['id'])) {
throw new \InvalidArgumentException(sprintf('Either "group", or "id" must be set for each user, but got %s.', json_encode($user)));
}
$this->users[] = $user;
}
}
/**
* {@inheritdoc}
*/
public function read($key)
{
return $this->delegate->read($key);
}
/**
* {@inheritdoc}
*/
public function rename($key, $new)
{
$rs = $this->delegate->rename($key, $new);
try {
$this->updateAcl($new);
return $rs;
} catch (\Exception $ex) {
$this->delete($new);
return false;
}
}
/**
* {@inheritdoc}
*/
public function write($key, $content)
{
$rs = $this->delegate->write($key, $content);
try {
$this->updateAcl($key);
return $rs;
} catch (\Exception $ex) {
$this->delete($key);
return false;
}
}
/**
* {@inheritdoc}
*/
public function exists($key)
{
return $this->delegate->exists($key);
}
/**
* {@inheritdoc}
*/
public function mtime($key)
{
return $this->delegate->mtime($key);
}
/**
* {@inheritdoc}
*/
public function keys()
{
return $this->delegate->keys();
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
return $this->delegate->delete($key);
}
/**
* {@inheritdoc}
*/
public function setMetadata($key, $metadata)
{
if ($this->delegate instanceof MetadataSupporter) {
$this->delegate->setMetadata($key, $metadata);
}
}
/**
* {@inheritdoc}
*/
public function getMetadata($key)
{
if ($this->delegate instanceof MetadataSupporter) {
return $this->delegate->getMetadata($key);
}
return array();
}
/**
* {@inheritdoc}
*/
public function isDirectory($key)
{
return $this->delegate->isDirectory($key);
}
protected function getAcl()
{
if (empty($this->users)) {
return $this->aclConstant;
}
return $this->users;
}
private function updateAcl($key)
{
$response = $this->s3->set_object_acl($this->bucketName, $key, $this->getAcl());
if (200 != $response->status) {
throw new \RuntimeException('S3-ACL change failed: '.print_r($response, true));
}
}
}

View File

@@ -0,0 +1,307 @@
<?php
namespace Gaufrette\Adapter;
use AmazonS3 as AmazonClient;
use Gaufrette\Adapter;
@trigger_error('The '.__NAMESPACE__.'\AmazonS3 adapter is deprecated since version 0.4 and will be removed in 1.0. Use the AwsS3 adapter instead.', E_USER_DEPRECATED);
/**
* Amazon S3 adapter using the AWS SDK for PHP v1.x.
*
* See the AwsS3 adapter for using the AWS SDK for PHP v2.x.
*
* @author Antoine Hérault <antoine.herault@gmail.com>
* @author Leszek Prabucki <leszek.prabucki@gmail.com>
*
* @deprecated The AmazonS3 adapter is deprecated since version 0.4 and will be removed in 1.0. Use the AwsS3 adapter instead.
*/
class AmazonS3 implements Adapter,
MetadataSupporter
{
protected $service;
protected $bucket;
protected $ensureBucket = false;
protected $metadata;
protected $options;
public function __construct(AmazonClient $service, $bucket, $options = array())
{
$this->service = $service;
$this->bucket = $bucket;
$this->options = array_replace_recursive(
array('directory' => '', 'create' => false, 'region' => $service->hostname, 'acl' => AmazonClient::ACL_PUBLIC),
$options
);
}
/**
* Set the acl used when writing files.
*
* @param string $acl
*/
public function setAcl($acl)
{
$this->options['acl'] = $acl;
}
/**
* Get the acl used when writing files.
*
* @return string
*/
public function getAcl()
{
return $this->options['acl'];
}
/**
* Set the base directory the user will have access to.
*
* @param string $directory
*/
public function setDirectory($directory)
{
$this->options['directory'] = $directory;
}
/**
* Get the directory the user has access to.
*
* @return string
*/
public function getDirectory()
{
return $this->options['directory'];
}
/**
* {@inheritdoc}
*/
public function setMetadata($key, $metadata)
{
$path = $this->computePath($key);
$this->metadata[$path] = $metadata;
}
/**
* {@inheritdoc}
*/
public function getMetadata($key)
{
$path = $this->computePath($key);
return isset($this->metadata[$path]) ? $this->metadata[$path] : array();
}
/**
* {@inheritdoc}
*/
public function read($key)
{
$this->ensureBucketExists();
$response = $this->service->get_object(
$this->bucket,
$this->computePath($key),
$this->getMetadata($key)
);
if (!$response->isOK()) {
return false;
}
return $response->body;
}
/**
* {@inheritdoc}
*/
public function rename($sourceKey, $targetKey)
{
$this->ensureBucketExists();
$response = $this->service->copy_object(
array( // source
'bucket' => $this->bucket,
'filename' => $this->computePath($sourceKey),
),
array( // target
'bucket' => $this->bucket,
'filename' => $this->computePath($targetKey),
),
$this->getMetadata($sourceKey)
);
return $response->isOK() && $this->delete($sourceKey);
}
/**
* {@inheritdoc}
*/
public function write($key, $content)
{
$this->ensureBucketExists();
$opt = array_replace_recursive(
array('acl' => $this->options['acl']),
$this->getMetadata($key),
array('body' => $content)
);
$response = $this->service->create_object(
$this->bucket,
$this->computePath($key),
$opt
);
if (!$response->isOK()) {
return false;
};
return intval($response->header['x-aws-requestheaders']['Content-Length']);
}
/**
* {@inheritdoc}
*/
public function exists($key)
{
$this->ensureBucketExists();
return $this->service->if_object_exists(
$this->bucket,
$this->computePath($key)
);
}
/**
* {@inheritdoc}
*/
public function mtime($key)
{
$this->ensureBucketExists();
$response = $this->service->get_object_metadata(
$this->bucket,
$this->computePath($key),
$this->getMetadata($key)
);
return isset($response['Headers']['last-modified']) ? strtotime($response['Headers']['last-modified']) : false;
}
/**
* {@inheritdoc}
*/
public function keys()
{
$this->ensureBucketExists();
$list = $this->service->get_object_list($this->bucket);
$keys = array();
foreach ($list as $file) {
if ('.' !== $dirname = \Gaufrette\Util\Path::dirname($file)) {
$keys[] = $dirname;
}
$keys[] = $file;
}
sort($keys);
return $keys;
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
$this->ensureBucketExists();
$response = $this->service->delete_object(
$this->bucket,
$this->computePath($key),
$this->getMetadata($key)
);
return $response->isOK();
}
/**
* {@inheritdoc}
*/
public function isDirectory($key)
{
if ($this->exists($key.'/')) {
return true;
}
return false;
}
/**
* Ensures the specified bucket exists. If the bucket does not exists
* and the create parameter is set to true, it will try to create the
* bucket.
*
* @throws \RuntimeException if the bucket does not exists or could not be
* created
*/
private function ensureBucketExists()
{
if ($this->ensureBucket) {
return;
}
if (isset($this->options['region'])) {
$this->service->set_region($this->options['region']);
}
if ($this->service->if_bucket_exists($this->bucket)) {
$this->ensureBucket = true;
return;
}
if (!$this->options['create']) {
throw new \RuntimeException(sprintf(
'The configured bucket "%s" does not exist.',
$this->bucket
));
}
$response = $this->service->create_bucket(
$this->bucket,
$this->options['region']
);
if (!$response->isOK()) {
throw new \RuntimeException(sprintf(
'Failed to create the configured bucket "%s".',
$this->bucket
));
}
$this->ensureBucket = true;
}
/**
* Computes the path for the specified key taking the bucket in account.
*
* @param string $key The key for which to compute the path
*
* @return string
*/
private function computePath($key)
{
$directory = $this->getDirectory();
if (null === $directory || '' === $directory) {
return $key;
}
return sprintf('%s/%s', $directory, $key);
}
}

View File

@@ -0,0 +1,153 @@
<?php
namespace Gaufrette\Adapter;
use Gaufrette\Adapter;
use Gaufrette\Util;
@trigger_error('The '.__NAMESPACE__.'\Apc adapter is deprecated since version 0.4 and will be removed in 1.0.', E_USER_DEPRECATED);
/**
* Apc adapter, a non-persistent adapter for when this sort of thing is appropriate.
*
* @author Alexander Deruwe <alexander.deruwe@gmail.com>
* @author Antoine Hérault <antoine.herault@gmail.com>
* @author Leszek Prabucki <leszek.prabucki@gmail.com>
*
* @deprecated The Apc adapter is deprecated since version 0.4 and will be removed in 1.0.
*/
class Apc implements Adapter
{
protected $prefix;
protected $ttl;
/**
* @throws \RuntimeException
*
* @param string $prefix to avoid conflicts between filesystems
* @param int $ttl time to live, default is 0
*/
public function __construct($prefix, $ttl = 0)
{
if (!extension_loaded('apc')) {
throw new \RuntimeException('Unable to use Gaufrette\Adapter\Apc as the APC extension is not available.');
}
$this->prefix = $prefix;
$this->ttl = $ttl;
}
/**
* {@inheritdoc}
*/
public function read($key)
{
return apc_fetch($this->computePath($key));
}
/**
* {@inheritdoc}
*/
public function write($key, $content, array $metadata = null)
{
$result = apc_store($this->computePath($key), $content, $this->ttl);
if (!$result) {
return false;
}
return Util\Size::fromContent($content);
}
/**
* {@inheritdoc}
*/
public function exists($key)
{
return apc_exists($this->computePath($key));
}
/**
* {@inheritdoc}
*/
public function keys()
{
$cachedKeys = $this->getCachedKeysIterator();
if (null === $cachedKeys) {
return array();
}
$keys = array();
foreach ($cachedKeys as $key => $value) {
$pattern = sprintf('/^%s/', preg_quote($this->prefix, '/'));
$keys[] = preg_replace($pattern, '', $key);
}
sort($keys);
return $keys;
}
/**
* {@inheritdoc}
*/
public function mtime($key)
{
$cachedKeys = iterator_to_array($this->getCachedKeysIterator($key, APC_ITER_MTIME));
return $cachedKeys[$this->computePath($key)]['mtime'];
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
return apc_delete($this->computePath($key));
}
/**
* {@inheritdoc}
*/
public function rename($sourceKey, $targetKey)
{
// TODO: this probably allows for race conditions...
$written = $this->write($targetKey, $this->read($sourceKey));
$deleted = $this->delete($sourceKey);
return $written && $deleted;
}
/**
* {@inheritdoc}
*/
public function isDirectory($key)
{
return false;
}
/**
* Computes the path for the given key.
*
* @param string $key
*
* @return string
*/
public function computePath($key)
{
return $this->prefix.$key;
}
/**
* @param string $key - by default ''
* @param int $format - by default APC_ITER_NONE
*
* @return \APCIterator
*/
protected function getCachedKeysIterator($key = '', $format = APC_ITER_NONE)
{
$pattern = sprintf('/^%s/', preg_quote($this->prefix.$key, '/'));
return new \APCIterator('user', $pattern, $format);
}
}

View File

@@ -0,0 +1,378 @@
<?php
namespace Gaufrette\Adapter;
use Gaufrette\Adapter;
use Aws\S3\S3Client;
use Gaufrette\Util;
/**
* Amazon S3 adapter using the AWS SDK for PHP v2.x.
*
* @author Michael Dowling <mtdowling@gmail.com>
*/
class AwsS3 implements Adapter,
MetadataSupporter,
ListKeysAware,
SizeCalculator,
MimeTypeProvider
{
/** @var S3Client */
protected $service;
/** @var string */
protected $bucket;
/** @var array */
protected $options;
/** @var bool */
protected $bucketExists;
/** @var array */
protected $metadata = [];
/** @var bool */
protected $detectContentType;
/**
* @param S3Client $service
* @param string $bucket
* @param array $options
* @param bool $detectContentType
*/
public function __construct(S3Client $service, $bucket, array $options = [], $detectContentType = false)
{
$this->service = $service;
$this->bucket = $bucket;
$this->options = array_replace(
[
'create' => false,
'directory' => '',
'acl' => 'private',
],
$options
);
$this->detectContentType = $detectContentType;
}
/**
* Gets the publicly accessible URL of an Amazon S3 object.
*
* @param string $key Object key
* @param array $options Associative array of options used to buld the URL
* - expires: The time at which the URL should expire
* represented as a UNIX timestamp
* - Any options available in the Amazon S3 GetObject
* operation may be specified.
*
* @return string
*
* @deprecated 1.0 Resolving object path into URLs is out of the scope of this repository since v0.4. gaufrette/extras
* provides a Filesystem decorator with a regular resolve() method. You should use it instead.
*
* @see https://github.com/Gaufrette/extras
*/
public function getUrl($key, array $options = [])
{
@trigger_error(
E_USER_DEPRECATED,
'Using AwsS3::getUrl() method was deprecated since v0.4. Please chek gaufrette/extras package if you want this feature'
);
return $this->service->getObjectUrl(
$this->bucket,
$this->computePath($key),
isset($options['expires']) ? $options['expires'] : null,
$options
);
}
/**
* {@inheritdoc}
*/
public function setMetadata($key, $metadata)
{
// BC with AmazonS3 adapter
if (isset($metadata['contentType'])) {
$metadata['ContentType'] = $metadata['contentType'];
unset($metadata['contentType']);
}
$this->metadata[$key] = $metadata;
}
/**
* {@inheritdoc}
*/
public function getMetadata($key)
{
return isset($this->metadata[$key]) ? $this->metadata[$key] : [];
}
/**
* {@inheritdoc}
*/
public function read($key)
{
$this->ensureBucketExists();
$options = $this->getOptions($key);
try {
// Get remote object
$object = $this->service->getObject($options);
// If there's no metadata array set up for this object, set it up
if (!array_key_exists($key, $this->metadata) || !is_array($this->metadata[$key])) {
$this->metadata[$key] = [];
}
// Make remote ContentType metadata available locally
$this->metadata[$key]['ContentType'] = $object->get('ContentType');
return (string) $object->get('Body');
} catch (\Exception $e) {
return false;
}
}
/**
* {@inheritdoc}
*/
public function rename($sourceKey, $targetKey)
{
$this->ensureBucketExists();
$options = $this->getOptions(
$targetKey,
['CopySource' => $this->bucket.'/'.$this->computePath($sourceKey)]
);
try {
$this->service->copyObject(array_merge($options, $this->getMetadata($targetKey)));
return $this->delete($sourceKey);
} catch (\Exception $e) {
return false;
}
}
/**
* {@inheritdoc}
*/
public function write($key, $content)
{
$this->ensureBucketExists();
$options = $this->getOptions($key, ['Body' => $content]);
/*
* If the ContentType was not already set in the metadata, then we autodetect
* it to prevent everything being served up as binary/octet-stream.
*/
if (!isset($options['ContentType']) && $this->detectContentType) {
$options['ContentType'] = $this->guessContentType($content);
}
try {
$this->service->putObject($options);
if (is_resource($content)) {
return Util\Size::fromResource($content);
}
return Util\Size::fromContent($content);
} catch (\Exception $e) {
return false;
}
}
/**
* {@inheritdoc}
*/
public function exists($key)
{
return $this->service->doesObjectExist($this->bucket, $this->computePath($key));
}
/**
* {@inheritdoc}
*/
public function mtime($key)
{
try {
$result = $this->service->headObject($this->getOptions($key));
return strtotime($result['LastModified']);
} catch (\Exception $e) {
return false;
}
}
/**
* {@inheritdoc}
*/
public function size($key)
{
try {
$result = $this->service->headObject($this->getOptions($key));
return $result['ContentLength'];
} catch (\Exception $e) {
return false;
}
}
/**
* {@inheritdoc}
*/
public function keys()
{
return $this->listKeys();
}
/**
* {@inheritdoc}
*/
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'];
}
$keys = [];
$iter = $this->service->getIterator('ListObjects', $options);
foreach ($iter as $file) {
$keys[] = $this->computeKey($file['Key']);
}
return $keys;
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
try {
$this->service->deleteObject($this->getOptions($key));
return true;
} catch (\Exception $e) {
return false;
}
}
/**
* {@inheritdoc}
*/
public function isDirectory($key)
{
$result = $this->service->listObjects([
'Bucket' => $this->bucket,
'Prefix' => rtrim($this->computePath($key), '/').'/',
'MaxKeys' => 1,
]);
if (isset($result['Contents'])) {
if (is_array($result['Contents']) || $result['Contents'] instanceof \Countable) {
return count($result['Contents']) > 0;
}
}
return false;
}
/**
* Ensures the specified bucket exists. If the bucket does not exists
* and the create option is set to true, it will try to create the
* bucket. The bucket is created using the same region as the supplied
* client object.
*
* @throws \RuntimeException if the bucket does not exists or could not be
* created
*/
protected function ensureBucketExists()
{
if ($this->bucketExists) {
return true;
}
if ($this->bucketExists = $this->service->doesBucketExist($this->bucket)) {
return true;
}
if (!$this->options['create']) {
throw new \RuntimeException(sprintf(
'The configured bucket "%s" does not exist.',
$this->bucket
));
}
$this->service->createBucket([
'Bucket' => $this->bucket,
'LocationConstraint' => $this->service->getRegion()
]);
$this->bucketExists = true;
return true;
}
protected function getOptions($key, array $options = [])
{
$options['ACL'] = $this->options['acl'];
$options['Bucket'] = $this->bucket;
$options['Key'] = $this->computePath($key);
/*
* Merge global options for adapter, which are set in the constructor, with metadata.
* Metadata will override global options.
*/
$options = array_merge($this->options, $options, $this->getMetadata($key));
return $options;
}
protected function computePath($key)
{
if (empty($this->options['directory'])) {
return $key;
}
return sprintf('%s/%s', $this->options['directory'], $key);
}
/**
* Computes the key from the specified path.
*
* @param string $path
*
* return string
*/
protected function computeKey($path)
{
return ltrim(substr($path, strlen($this->options['directory'])), '/');
}
/**
* @param string $content
*
* @return string
*/
private function guessContentType($content)
{
$fileInfo = new \finfo(FILEINFO_MIME_TYPE);
if (is_resource($content)) {
return $fileInfo->file(stream_get_meta_data($content)['uri']);
}
return $fileInfo->buffer($content);
}
public function mimeType($key)
{
try {
$result = $this->service->headObject($this->getOptions($key));
return ($result['ContentType']);
} catch (\Exception $e) {
return false;
}
}
}

View File

@@ -0,0 +1,557 @@
<?php
namespace Gaufrette\Adapter;
use Gaufrette\Adapter;
use Gaufrette\Util;
use Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface;
use MicrosoftAzure\Storage\Blob\Models\Blob;
use MicrosoftAzure\Storage\Blob\Models\Container;
use MicrosoftAzure\Storage\Blob\Models\CreateBlobOptions;
use MicrosoftAzure\Storage\Blob\Models\CreateBlockBlobOptions;
use MicrosoftAzure\Storage\Blob\Models\CreateContainerOptions;
use MicrosoftAzure\Storage\Blob\Models\DeleteContainerOptions;
use MicrosoftAzure\Storage\Blob\Models\ListBlobsOptions;
use MicrosoftAzure\Storage\Common\Exceptions\ServiceException;
/**
* Microsoft Azure Blob Storage adapter.
*
* @author Luciano Mammino <lmammino@oryzone.com>
* @author Paweł Czyżewski <pawel.czyzewski@enginewerk.com>
*/
class AzureBlobStorage implements Adapter,
MetadataSupporter,
SizeCalculator
{
/**
* Error constants.
*/
const ERROR_CONTAINER_ALREADY_EXISTS = 'ContainerAlreadyExists';
const ERROR_CONTAINER_NOT_FOUND = 'ContainerNotFound';
/**
* @var AzureBlobStorage\BlobProxyFactoryInterface
*/
protected $blobProxyFactory;
/**
* @var string
*/
protected $containerName;
/**
* @var bool
*/
protected $detectContentType;
/**
* @var \MicrosoftAzure\Storage\Blob\Internal\IBlob
*/
protected $blobProxy;
/**
* @var bool
*/
protected $multiContainerMode = false;
/**
* @var CreateContainerOptions
*/
protected $createContainerOptions;
/**
* @param AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
* @param string|null $containerName
* @param bool $create
* @param bool $detectContentType
*
* @throws \RuntimeException
*/
public function __construct(BlobProxyFactoryInterface $blobProxyFactory, $containerName = null, $create = false, $detectContentType = true)
{
$this->blobProxyFactory = $blobProxyFactory;
$this->containerName = $containerName;
$this->detectContentType = $detectContentType;
if (null === $containerName) {
$this->multiContainerMode = true;
} elseif ($create) {
$this->createContainer($containerName);
}
}
/**
* @return CreateContainerOptions
*/
public function getCreateContainerOptions()
{
return $this->createContainerOptions;
}
/**
* @param CreateContainerOptions $options
*/
public function setCreateContainerOptions(CreateContainerOptions $options)
{
$this->createContainerOptions = $options;
}
/**
* Creates a new container.
*
* @param string $containerName
* @param \MicrosoftAzure\Storage\Blob\Models\CreateContainerOptions $options
*
* @throws \RuntimeException if cannot create the container
*/
public function createContainer($containerName, CreateContainerOptions $options = null)
{
$this->init();
if (null === $options) {
$options = $this->getCreateContainerOptions();
}
try {
$this->blobProxy->createContainer($containerName, $options);
} catch (ServiceException $e) {
$errorCode = $this->getErrorCodeFromServiceException($e);
if ($errorCode !== self::ERROR_CONTAINER_ALREADY_EXISTS) {
throw new \RuntimeException(sprintf(
'Failed to create the configured container "%s": %s (%s).',
$containerName,
$e->getErrorText(),
$errorCode
));
}
}
}
/**
* Deletes a container.
*
* @param string $containerName
* @param DeleteContainerOptions $options
*
* @throws \RuntimeException if cannot delete the container
*/
public function deleteContainer($containerName, DeleteContainerOptions $options = null)
{
$this->init();
try {
$this->blobProxy->deleteContainer($containerName, $options);
} catch (ServiceException $e) {
$errorCode = $this->getErrorCodeFromServiceException($e);
if ($errorCode !== self::ERROR_CONTAINER_NOT_FOUND) {
throw new \RuntimeException(sprintf(
'Failed to delete the configured container "%s": %s (%s).',
$containerName,
$e->getErrorText(),
$errorCode
), $e->getCode());
}
}
}
/**
* {@inheritdoc}
* @throws \RuntimeException
* @throws \InvalidArgumentException
*/
public function read($key)
{
$this->init();
list($containerName, $key) = $this->tokenizeKey($key);
try {
$blob = $this->blobProxy->getBlob($containerName, $key);
return stream_get_contents($blob->getContentStream());
} catch (ServiceException $e) {
$this->failIfContainerNotFound($e, sprintf('read key "%s"', $key), $containerName);
return false;
}
}
/**
* {@inheritdoc}
* @throws \RuntimeException
* @throws \InvalidArgumentException
*/
public function write($key, $content)
{
$this->init();
list($containerName, $key) = $this->tokenizeKey($key);
if (class_exists(CreateBlockBlobOptions::class)) {
$options = new CreateBlockBlobOptions();
} else {
// for microsoft/azure-storage < 1.0
$options = new CreateBlobOptions();
}
if ($this->detectContentType) {
$contentType = $this->guessContentType($content);
$options->setContentType($contentType);
}
try {
if ($this->multiContainerMode) {
$this->createContainer($containerName);
}
$this->blobProxy->createBlockBlob($containerName, $key, $content, $options);
} catch (ServiceException $e) {
$this->failIfContainerNotFound($e, sprintf('write content for key "%s"', $key), $containerName);
return false;
}
if (is_resource($content)) {
return Util\Size::fromResource($content);
}
return Util\Size::fromContent($content);
}
/**
* {@inheritdoc}
* @throws \RuntimeException
* @throws \InvalidArgumentException
*/
public function exists($key)
{
$this->init();
list($containerName, $key) = $this->tokenizeKey($key);
$listBlobsOptions = new ListBlobsOptions();
$listBlobsOptions->setPrefix($key);
try {
$blobsList = $this->blobProxy->listBlobs($containerName, $listBlobsOptions);
foreach ($blobsList->getBlobs() as $blob) {
if ($key === $blob->getName()) {
return true;
}
}
} catch (ServiceException $e) {
$errorCode = $this->getErrorCodeFromServiceException($e);
if ($this->multiContainerMode && self::ERROR_CONTAINER_NOT_FOUND === $errorCode) {
return false;
}
$this->failIfContainerNotFound($e, 'check if key exists', $containerName);
throw new \RuntimeException(sprintf(
'Failed to check if key "%s" exists in container "%s": %s (%s).',
$key,
$containerName,
$e->getErrorText(),
$errorCode
), $e->getCode());
}
return false;
}
/**
* {@inheritdoc}
* @throws \RuntimeException
*/
public function keys()
{
$this->init();
try {
if ($this->multiContainerMode) {
$containersList = $this->blobProxy->listContainers();
return call_user_func_array('array_merge', array_map(
function(Container $container) {
$containerName = $container->getName();
return $this->fetchBlobs($containerName, $containerName);
},
$containersList->getContainers()
));
}
return $this->fetchBlobs($this->containerName);
} catch (ServiceException $e) {
$this->failIfContainerNotFound($e, 'retrieve keys', $this->containerName);
$errorCode = $this->getErrorCodeFromServiceException($e);
throw new \RuntimeException(sprintf(
'Failed to list keys for the container "%s": %s (%s).',
$this->containerName,
$e->getErrorText(),
$errorCode
), $e->getCode());
}
}
/**
* {@inheritdoc}
* @throws \RuntimeException
* @throws \InvalidArgumentException
*/
public function mtime($key)
{
$this->init();
list($containerName, $key) = $this->tokenizeKey($key);
try {
$properties = $this->blobProxy->getBlobProperties($containerName, $key);
return $properties->getProperties()->getLastModified()->getTimestamp();
} catch (ServiceException $e) {
$this->failIfContainerNotFound($e, sprintf('read mtime for key "%s"', $key), $containerName);
return false;
}
}
/**
* {@inheritdoc}
*/
public function size($key)
{
$this->init();
list($containerName, $key) = $this->tokenizeKey($key);
try {
$properties = $this->blobProxy->getBlobProperties($containerName, $key);
return $properties->getProperties()->getContentLength();
} catch (ServiceException $e) {
$this->failIfContainerNotFound($e, sprintf('read content length for key "%s"', $key), $containerName);
return false;
}
}
/**
* {@inheritdoc}
* @throws \RuntimeException
* @throws \InvalidArgumentException
*/
public function delete($key)
{
$this->init();
list($containerName, $key) = $this->tokenizeKey($key);
try {
$this->blobProxy->deleteBlob($containerName, $key);
return true;
} catch (ServiceException $e) {
$this->failIfContainerNotFound($e, sprintf('delete key "%s"', $key), $containerName);
return false;
}
}
/**
* {@inheritdoc}
* @throws \RuntimeException
* @throws \InvalidArgumentException
*/
public function rename($sourceKey, $targetKey)
{
$this->init();
list($sourceContainerName, $sourceKey) = $this->tokenizeKey($sourceKey);
list($targetContainerName, $targetKey) = $this->tokenizeKey($targetKey);
try {
if ($this->multiContainerMode) {
$this->createContainer($targetContainerName);
}
$this->blobProxy->copyBlob($targetContainerName, $targetKey, $sourceContainerName, $sourceKey);
$this->blobProxy->deleteBlob($sourceContainerName, $sourceKey);
return true;
} catch (ServiceException $e) {
$this->failIfContainerNotFound($e, sprintf('rename key "%s"', $sourceKey), $sourceContainerName);
return false;
}
}
/**
* {@inheritdoc}
*/
public function isDirectory($key)
{
// Windows Azure Blob Storage does not support directories
return false;
}
/**
* {@inheritdoc}
* @throws \RuntimeException
* @throws \InvalidArgumentException
*/
public function setMetadata($key, $content)
{
$this->init();
list($containerName, $key) = $this->tokenizeKey($key);
try {
$this->blobProxy->setBlobMetadata($containerName, $key, $content);
} catch (ServiceException $e) {
$errorCode = $this->getErrorCodeFromServiceException($e);
throw new \RuntimeException(sprintf(
'Failed to set metadata for blob "%s" in container "%s": %s (%s).',
$key,
$containerName,
$e->getErrorText(),
$errorCode
), $e->getCode());
}
}
/**
* {@inheritdoc}
* @throws \RuntimeException
* @throws \InvalidArgumentException
*/
public function getMetadata($key)
{
$this->init();
list($containerName, $key) = $this->tokenizeKey($key);
try {
$properties = $this->blobProxy->getBlobProperties($containerName, $key);
return $properties->getMetadata();
} catch (ServiceException $e) {
$errorCode = $this->getErrorCodeFromServiceException($e);
throw new \RuntimeException(sprintf(
'Failed to get metadata for blob "%s" in container "%s": %s (%s).',
$key,
$containerName,
$e->getErrorText(),
$errorCode
), $e->getCode());
}
}
/**
* Lazy initialization, automatically called when some method is called after construction.
*/
protected function init()
{
if ($this->blobProxy === null) {
$this->blobProxy = $this->blobProxyFactory->create();
}
}
/**
* Throws a runtime exception if a give ServiceException derived from a "container not found" error.
*
* @param ServiceException $exception
* @param string $action
* @param string $containerName
*
* @throws \RuntimeException
*/
protected function failIfContainerNotFound(ServiceException $exception, $action, $containerName)
{
$errorCode = $this->getErrorCodeFromServiceException($exception);
if ($errorCode === self::ERROR_CONTAINER_NOT_FOUND) {
throw new \RuntimeException(sprintf(
'Failed to %s: container "%s" not found.',
$action,
$containerName
), $exception->getCode());
}
}
/**
* Extracts the error code from a service exception.
*
* @param ServiceException $exception
*
* @return string
*/
protected function getErrorCodeFromServiceException(ServiceException $exception)
{
$xml = @simplexml_load_string($exception->getResponse()->getBody());
if ($xml && isset($xml->Code)) {
return (string) $xml->Code;
}
return $exception->getErrorText();
}
/**
* @param string|resource $content
*
* @return string
*/
private function guessContentType($content)
{
$fileInfo = new \finfo(FILEINFO_MIME_TYPE);
if (is_resource($content)) {
return $fileInfo->file(stream_get_meta_data($content)['uri']);
}
return $fileInfo->buffer($content);
}
/**
* @param string $key
*
* @return array
* @throws \InvalidArgumentException
*/
private function tokenizeKey($key)
{
$containerName = $this->containerName;
if (false === $this->multiContainerMode) {
return [$containerName, $key];
}
if (false === ($index = strpos($key, '/'))) {
throw new \InvalidArgumentException(sprintf(
'Failed to establish container name from key "%s", container name is required in multi-container mode',
$key
));
}
$containerName = substr($key, 0, $index);
$key = substr($key, $index + 1);
return [$containerName, $key];
}
/**
* @param string $containerName
* @param null $prefix
*
* @return array
*/
private function fetchBlobs($containerName, $prefix = null)
{
$blobList = $this->blobProxy->listBlobs($containerName);
return array_map(
function (Blob $blob) use ($prefix) {
$name = $blob->getName();
if (null !== $prefix) {
$name = $prefix .'/'. $name;
}
return $name;
},
$blobList->getBlobs()
);
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace Gaufrette\Adapter\AzureBlobStorage;
use MicrosoftAzure\Storage\Blob\BlobRestProxy;
use MicrosoftAzure\Storage\Common\ServicesBuilder;
/**
* Basic implementation for a Blob proxy factory.
*
* @author Luciano Mammino <lmammino@oryzone.com>
*/
class BlobProxyFactory implements BlobProxyFactoryInterface
{
/**
* @var string
*/
protected $connectionString;
/**
* @param string $connectionString
*/
public function __construct($connectionString)
{
$this->connectionString = $connectionString;
}
/**
* {@inheritdoc}
*/
public function create()
{
if (class_exists(ServicesBuilder::class)) {
// for microsoft/azure-storage < 1.0
return ServicesBuilder::getInstance()->createBlobService($this->connectionString);
} else {
return BlobRestProxy::createBlobService($this->connectionString);
}
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Gaufrette\Adapter\AzureBlobStorage;
/**
* Interface to define Blob proxy factories.
*
* @author Luciano Mammino <lmammino@oryzone.com>
*/
interface BlobProxyFactoryInterface
{
/**
* Creates a new instance of the Blob proxy.
*
* @return \MicrosoftAzure\Storage\Blob\Internal\IBlob
*/
public function create();
}

View File

@@ -0,0 +1,244 @@
<?php
namespace Gaufrette\Adapter;
use Gaufrette\File;
use Gaufrette\Adapter;
use Gaufrette\Adapter\InMemory as InMemoryAdapter;
@trigger_error('The '.__NAMESPACE__.'\Cache adapter is deprecated since version 0.4 and will be removed in 1.0.', E_USER_DEPRECATED);
/**
* Cache adapter.
*
* @author Antoine Hérault <antoine.herault@gmail.com>
*
* @deprecated The Cache adapter is deprecated since version 0.4 and will be removed in 1.0.
*/
class Cache implements Adapter,
MetadataSupporter
{
/**
* @var Adapter
*/
protected $source;
/**
* @var Adapter
*/
protected $cache;
/**
* @var int
*/
protected $ttl;
/**
* @var Adapter
*/
protected $serializeCache;
/**
* @param Adapter $source The source adapter that must be cached
* @param Adapter $cache The adapter used to cache the source
* @param int $ttl Time to live of a cached file
* @param Adapter $serializeCache The adapter used to cache serializations
*/
public function __construct(Adapter $source, Adapter $cache, $ttl = 0, Adapter $serializeCache = null)
{
$this->source = $source;
$this->cache = $cache;
$this->ttl = $ttl;
if (!$serializeCache) {
$serializeCache = new InMemoryAdapter();
}
$this->serializeCache = $serializeCache;
}
/**
* Returns the time to live of the cache.
*
* @return int $ttl
*/
public function getTtl()
{
return $this->ttl;
}
/**
* Defines the time to live of the cache.
*
* @param int $ttl
*/
public function setTtl($ttl)
{
$this->ttl = $ttl;
}
/**
* {@inheritdoc}
*/
public function read($key)
{
if ($this->needsReload($key)) {
$contents = $this->source->read($key);
$this->cache->write($key, $contents);
} else {
$contents = $this->cache->read($key);
}
return $contents;
}
/**
* {@inheritdoc}
*/
public function rename($key, $new)
{
return $this->source->rename($key, $new) && $this->cache->rename($key, $new);
}
/**
* {@inheritdoc}
*/
public function write($key, $content, array $metadata = null)
{
$bytesSource = $this->source->write($key, $content);
if (false === $bytesSource) {
return false;
}
$bytesCache = $this->cache->write($key, $content);
if ($bytesSource !== $bytesCache) {
return false;
}
return $bytesSource;
}
/**
* {@inheritdoc}
*/
public function exists($key)
{
if ($this->needsReload($key)) {
return $this->source->exists($key);
}
return $this->cache->exists($key);
}
/**
* {@inheritdoc}
*/
public function mtime($key)
{
return $this->source->mtime($key);
}
/**
* {@inheritdoc}
*/
public function keys()
{
$cacheFile = 'keys.cache';
if ($this->needsRebuild($cacheFile)) {
$keys = $this->source->keys();
sort($keys);
$this->serializeCache->write($cacheFile, serialize($keys));
} else {
$keys = unserialize($this->serializeCache->read($cacheFile));
}
return $keys;
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
return $this->source->delete($key) && $this->cache->delete($key);
}
/**
* {@inheritdoc}
*/
public function isDirectory($key)
{
return $this->source->isDirectory($key);
}
/**
* {@inheritdoc}
*/
public function setMetadata($key, $metadata)
{
if ($this->source instanceof MetadataSupporter) {
$this->source->setMetadata($key, $metadata);
}
if ($this->cache instanceof MetadataSupporter) {
$this->cache->setMetadata($key, $metadata);
}
}
/**
* {@inheritdoc}
*/
public function getMetadata($key)
{
if ($this->source instanceof MetadataSupporter) {
return $this->source->getMetadata($key);
}
return false;
}
/**
* Indicates whether the cache for the specified key needs to be reloaded.
*
* @param string $key
*/
public function needsReload($key)
{
$needsReload = true;
if ($this->cache->exists($key)) {
try {
$dateCache = $this->cache->mtime($key);
$needsReload = false;
if (time() - $this->ttl >= $dateCache) {
$dateSource = $this->source->mtime($key);
$needsReload = $dateCache < $dateSource;
}
} catch (\RuntimeException $e) {
}
}
return $needsReload;
}
/**
* Indicates whether the serialized cache file needs to be rebuild.
*
* @param string $cacheFile
*/
public function needsRebuild($cacheFile)
{
$needsRebuild = true;
if ($this->serializeCache->exists($cacheFile)) {
try {
$needsRebuild = time() - $this->ttl >= $this->serializeCache->mtime($cacheFile);
} catch (\RuntimeException $e) {
}
}
return $needsRebuild;
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Gaufrette\Adapter;
/**
* Interface which add checksum calculation support to adapter.
*
* @author Leszek Prabucki <leszek.prabucki@gmail.com>
*/
interface ChecksumCalculator
{
/**
* Returns the checksum of the specified key.
*
* @param string $key
*
* @return string
*/
public function checksum($key);
}

View File

@@ -0,0 +1,202 @@
<?php
namespace Gaufrette\Adapter;
use Gaufrette\Adapter;
use Gaufrette\Util;
use Doctrine\DBAL\Connection;
/**
* Doctrine DBAL adapter.
*
* @author Markus Bachmann <markus.bachmann@bachi.biz>
* @author Antoine Hérault <antoine.herault@gmail.com>
* @author Leszek Prabucki <leszek.prabucki@gmail.com>
*/
class DoctrineDbal implements Adapter,
ChecksumCalculator,
ListKeysAware
{
protected $connection;
protected $table;
protected $columns = array(
'key' => 'key',
'content' => 'content',
'mtime' => 'mtime',
'checksum' => 'checksum',
);
/**
* @param Connection $connection The DBAL connection
* @param string $table The files table
* @param array $columns The column names
*/
public function __construct(Connection $connection, $table, array $columns = array())
{
$this->connection = $connection;
$this->table = $table;
$this->columns = array_replace($this->columns, $columns);
}
/**
* {@inheritdoc}
*/
public function keys()
{
$keys = array();
$stmt = $this->connection->executeQuery(sprintf(
'SELECT %s FROM %s',
$this->getQuotedColumn('key'),
$this->getQuotedTable()
));
return $stmt->fetchAll(\PDO::FETCH_COLUMN);
}
/**
* {@inheritdoc}
*/
public function rename($sourceKey, $targetKey)
{
return (boolean) $this->connection->update(
$this->table,
array($this->getQuotedColumn('key') => $targetKey),
array($this->getQuotedColumn('key') => $sourceKey)
);
}
/**
* {@inheritdoc}
*/
public function mtime($key)
{
return $this->getColumnValue($key, 'mtime');
}
/**
* {@inheritdoc}
*/
public function checksum($key)
{
return $this->getColumnValue($key, 'checksum');
}
/**
* {@inheritdoc}
*/
public function exists($key)
{
return (boolean) $this->connection->fetchColumn(
sprintf(
'SELECT COUNT(%s) FROM %s WHERE %s = :key',
$this->getQuotedColumn('key'),
$this->getQuotedTable(),
$this->getQuotedColumn('key')
),
array('key' => $key)
);
}
/**
* {@inheritdoc}
*/
public function read($key)
{
return $this->getColumnValue($key, 'content');
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
return (boolean) $this->connection->delete(
$this->table,
array($this->getQuotedColumn('key') => $key)
);
}
/**
* {@inheritdoc}
*/
public function write($key, $content)
{
$values = array(
$this->getQuotedColumn('content') => $content,
$this->getQuotedColumn('mtime') => time(),
$this->getQuotedColumn('checksum') => Util\Checksum::fromContent($content),
);
if ($this->exists($key)) {
$this->connection->update(
$this->table,
$values,
array($this->getQuotedColumn('key') => $key)
);
} else {
$values[$this->getQuotedColumn('key')] = $key;
$this->connection->insert($this->table, $values);
}
return Util\Size::fromContent($content);
}
/**
* {@inheritdoc}
*/
public function isDirectory($key)
{
return false;
}
private function getColumnValue($key, $column)
{
$value = $this->connection->fetchColumn(
sprintf(
'SELECT %s FROM %s WHERE %s = :key',
$this->getQuotedColumn($column),
$this->getQuotedTable(),
$this->getQuotedColumn('key')
),
array('key' => $key)
);
return $value;
}
/**
* {@inheritdoc}
*/
public function listKeys($prefix = '')
{
$prefix = trim($prefix);
$keys = $this->connection->fetchAll(
sprintf(
'SELECT %s AS _key FROM %s WHERE %s LIKE :pattern',
$this->getQuotedColumn('key'),
$this->getQuotedTable(),
$this->getQuotedColumn('key')
),
array('pattern' => sprintf('%s%%', $prefix))
);
return array(
'dirs' => array(),
'keys' => array_map(function ($value) {
return $value['_key'];
},
$keys),
);
}
private function getQuotedTable()
{
return $this->connection->quoteIdentifier($this->table);
}
private function getQuotedColumn($column)
{
return $this->connection->quoteIdentifier($this->columns[$column]);
}
}

View File

@@ -0,0 +1,182 @@
<?php
namespace Gaufrette\Adapter;
use Gaufrette\Adapter;
use Gaufrette\Util;
use Gaufrette\Exception;
use Dropbox_API as DropboxApi;
use Dropbox_Exception_NotFound as DropboxNotFoundException;
@trigger_error('The '.__NAMESPACE__.'\Dropbox adapter is deprecated since version 0.4 and will be removed in 1.0. You can move to our Flysystem adapter and use their Dropbox adapter if needed.', E_USER_DEPRECATED);
/**
* Dropbox adapter.
*
* @author Markus Bachmann <markus.bachmann@bachi.biz>
* @author Antoine Hérault <antoine.herault@gmail.com>
* @author Leszek Prabucki <leszek.prabucki@gmail.com>
*
* @deprecated The Dropbox adapter is deprecated since version 0.4 and will be removed in 1.0.
*/
class Dropbox implements Adapter
{
protected $client;
/**
* @param \Dropbox_API $client The Dropbox API client
*/
public function __construct(DropboxApi $client)
{
$this->client = $client;
}
/**
* {@inheritdoc}
*
* @throws \Dropbox_Exception_Forbidden
* @throws \Dropbox_Exception_OverQuota
* @throws \OAuthException
*/
public function read($key)
{
try {
return $this->client->getFile($key);
} catch (DropboxNotFoundException $e) {
return false;
}
}
/**
* {@inheritdoc}
*/
public function isDirectory($key)
{
try {
$metadata = $this->getDropboxMetadata($key);
} catch (Exception\FileNotFound $e) {
return false;
}
return (boolean) isset($metadata['is_dir']) ? $metadata['is_dir'] : false;
}
/**
* {@inheritdoc}
*
* @throws \Dropbox_Exception
*/
public function write($key, $content)
{
$resource = tmpfile();
fwrite($resource, $content);
fseek($resource, 0);
try {
$this->client->putFile($key, $resource);
} catch (\Exception $e) {
fclose($resource);
throw $e;
}
fclose($resource);
return Util\Size::fromContent($content);
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
try {
$this->client->delete($key);
} catch (DropboxNotFoundException $e) {
return false;
}
return true;
}
/**
* {@inheritdoc}
*/
public function rename($sourceKey, $targetKey)
{
try {
$this->client->move($sourceKey, $targetKey);
} catch (DropboxNotFoundException $e) {
return false;
}
return true;
}
/**
* {@inheritdoc}
*/
public function mtime($key)
{
try {
$metadata = $this->getDropboxMetadata($key);
} catch (Exception\FileNotFound $e) {
return false;
}
return strtotime($metadata['modified']);
}
/**
* {@inheritdoc}
*/
public function keys()
{
$metadata = $this->client->getMetaData('/', true);
if (!isset($metadata['contents'])) {
return array();
}
$keys = array();
foreach ($metadata['contents'] as $value) {
$file = ltrim($value['path'], '/');
$keys[] = $file;
if ('.' !== $dirname = \Gaufrette\Util\Path::dirname($file)) {
$keys[] = $dirname;
}
}
sort($keys);
return $keys;
}
/**
* {@inheritdoc}
*/
public function exists($key)
{
try {
$this->getDropboxMetadata($key);
return true;
} catch (Exception\FileNotFound $e) {
return false;
}
}
private function getDropboxMetadata($key)
{
try {
$metadata = $this->client->getMetaData($key, false);
} catch (DropboxNotFoundException $e) {
throw new Exception\FileNotFound($key, 0, $e);
}
// TODO find a way to exclude deleted files
if (isset($metadata['is_deleted']) && $metadata['is_deleted']) {
throw new Exception\FileNotFound($key);
}
return $metadata;
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Gaufrette\Adapter;
use Gaufrette\File;
use Gaufrette\Filesystem;
/**
* Interface for the file creation class.
*
* @author Leszek Prabucki <leszek.prabucki@gmail.com>
*/
interface FileFactory
{
/**
* Creates a new File instance and returns it.
*
* @param string $key
* @param Filesystem $filesystem
*
* @return File
*/
public function createFile($key, Filesystem $filesystem);
}

View File

@@ -0,0 +1,121 @@
<?php
namespace Gaufrette\Adapter;
use Gaufrette\Adapter;
use Gaufrette\Exception\UnsupportedAdapterMethodException;
use League\Flysystem\AdapterInterface;
use League\Flysystem\Util;
class Flysystem implements Adapter, ListKeysAware
{
/**
* @var AdapterInterface
*/
private $adapter;
/**
* @var Config
*/
private $config;
/**
* @param AdapterInterface $adapter
* @param Config|array|null $config
*/
public function __construct(AdapterInterface $adapter, $config = null)
{
$this->adapter = $adapter;
$this->config = Util::ensureConfig($config);
}
/**
* {@inheritdoc}
*/
public function read($key)
{
return $this->adapter->read($key)['contents'];
}
/**
* {@inheritdoc}
*/
public function write($key, $content)
{
return $this->adapter->write($key, $content, $this->config);
}
/**
* {@inheritdoc}
*/
public function exists($key)
{
return (bool) $this->adapter->has($key);
}
/**
* {@inheritdoc}
*/
public function keys()
{
return array_map(function ($content) {
return $content['path'];
}, $this->adapter->listContents());
}
/**
* {@inheritdoc}
*/
public function listKeys($prefix = '')
{
$dirs = [];
$keys = [];
foreach ($this->adapter->listContents() as $content) {
if (empty($prefix) || 0 === strpos($content['path'], $prefix)) {
if ('dir' === $content['type']) {
$dirs[] = $content['path'];
} else {
$keys[] = $content['path'];
}
}
}
return [
'keys' => $keys,
'dirs' => $dirs,
];
}
/**
* {@inheritdoc}
*/
public function mtime($key)
{
return $this->adapter->getTimestamp($key);
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
return $this->adapter->delete($key);
}
/**
* {@inheritdoc}
*/
public function rename($sourceKey, $targetKey)
{
return $this->adapter->rename($sourceKey, $targetKey);
}
/**
* {@inheritdoc}
*/
public function isDirectory($key)
{
throw new UnsupportedAdapterMethodException('isDirectory is not supported by this adapter.');
}
}

View File

@@ -0,0 +1,590 @@
<?php
namespace Gaufrette\Adapter;
use Gaufrette\Adapter;
use Gaufrette\File;
use Gaufrette\Filesystem;
/**
* Ftp adapter.
*
* @author Antoine Hérault <antoine.herault@gmail.com>
*/
class Ftp implements Adapter,
FileFactory,
ListKeysAware,
SizeCalculator
{
protected $connection = null;
protected $directory;
protected $host;
protected $port;
protected $username;
protected $password;
protected $passive;
protected $create;
protected $mode;
protected $ssl;
protected $timeout;
protected $fileData = array();
protected $utf8;
/**
* @param string $directory The directory to use in the ftp server
* @param string $host The host of the ftp server
* @param array $options The options like port, username, password, passive, create, mode
*/
public function __construct($directory, $host, $options = array())
{
if (!extension_loaded('ftp')) {
throw new \RuntimeException('Unable to use Gaufrette\Adapter\Ftp as the FTP extension is not available.');
}
$this->directory = (string) $directory;
$this->host = $host;
$this->port = isset($options['port']) ? $options['port'] : 21;
$this->username = isset($options['username']) ? $options['username'] : null;
$this->password = isset($options['password']) ? $options['password'] : null;
$this->passive = isset($options['passive']) ? $options['passive'] : false;
$this->create = isset($options['create']) ? $options['create'] : false;
$this->mode = isset($options['mode']) ? $options['mode'] : FTP_BINARY;
$this->ssl = isset($options['ssl']) ? $options['ssl'] : false;
$this->timeout = isset($options['timeout']) ? $options['timeout'] : 90;
$this->utf8 = isset($options['utf8']) ? $options['utf8'] : false;
}
/**
* {@inheritdoc}
*/
public function read($key)
{
$this->ensureDirectoryExists($this->directory, $this->create);
$temp = fopen('php://temp', 'r+');
if (!ftp_fget($this->getConnection(), $temp, $this->computePath($key), $this->mode)) {
return false;
}
rewind($temp);
$contents = stream_get_contents($temp);
fclose($temp);
return $contents;
}
/**
* {@inheritdoc}
*/
public function write($key, $content)
{
$this->ensureDirectoryExists($this->directory, $this->create);
$path = $this->computePath($key);
$directory = \Gaufrette\Util\Path::dirname($path);
$this->ensureDirectoryExists($directory, true);
$temp = fopen('php://temp', 'r+');
$size = fwrite($temp, $content);
rewind($temp);
if (!ftp_fput($this->getConnection(), $path, $temp, $this->mode)) {
fclose($temp);
return false;
}
fclose($temp);
return $size;
}
/**
* {@inheritdoc}
*/
public function rename($sourceKey, $targetKey)
{
$this->ensureDirectoryExists($this->directory, $this->create);
$sourcePath = $this->computePath($sourceKey);
$targetPath = $this->computePath($targetKey);
$this->ensureDirectoryExists(\Gaufrette\Util\Path::dirname($targetPath), true);
return ftp_rename($this->getConnection(), $sourcePath, $targetPath);
}
/**
* {@inheritdoc}
*/
public function exists($key)
{
$this->ensureDirectoryExists($this->directory, $this->create);
$file = $this->computePath($key);
$lines = ftp_rawlist($this->getConnection(), '-al ' . \Gaufrette\Util\Path::dirname($file));
if (false === $lines) {
return false;
}
$pattern = '{(?<!->) '.preg_quote(basename($file)).'( -> |$)}m';
foreach ($lines as $line) {
if (preg_match($pattern, $line)) {
return true;
}
}
return false;
}
/**
* {@inheritdoc}
*/
public function keys()
{
$this->ensureDirectoryExists($this->directory, $this->create);
$keys = $this->fetchKeys();
return $keys['keys'];
}
/**
* {@inheritdoc}
*/
public function listKeys($prefix = '')
{
$this->ensureDirectoryExists($this->directory, $this->create);
preg_match('/(.*?)[^\/]*$/', $prefix, $match);
$directory = rtrim($match[1], '/');
$keys = $this->fetchKeys($directory, false);
if ($directory === $prefix) {
return $keys;
}
$filteredKeys = array();
foreach (array('keys', 'dirs') as $hash) {
$filteredKeys[$hash] = array();
foreach ($keys[$hash] as $key) {
if (0 === strpos($key, $prefix)) {
$filteredKeys[$hash][] = $key;
}
}
}
return $filteredKeys;
}
/**
* {@inheritdoc}
*/
public function mtime($key)
{
$this->ensureDirectoryExists($this->directory, $this->create);
$mtime = ftp_mdtm($this->getConnection(), $this->computePath($key));
// the server does not support this function
if (-1 === $mtime) {
throw new \RuntimeException('Server does not support ftp_mdtm function.');
}
return $mtime;
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
$this->ensureDirectoryExists($this->directory, $this->create);
if ($this->isDirectory($key)) {
return ftp_rmdir($this->getConnection(), $this->computePath($key));
}
return ftp_delete($this->getConnection(), $this->computePath($key));
}
/**
* {@inheritdoc}
*/
public function isDirectory($key)
{
$this->ensureDirectoryExists($this->directory, $this->create);
return $this->isDir($this->computePath($key));
}
/**
* Lists files from the specified directory. If a pattern is
* specified, it only returns files matching it.
*
* @param string $directory The path of the directory to list from
*
* @return array An array of keys and dirs
*/
public function listDirectory($directory = '')
{
$this->ensureDirectoryExists($this->directory, $this->create);
$directory = preg_replace('/^[\/]*([^\/].*)$/', '/$1', $directory);
$items = $this->parseRawlist(
ftp_rawlist($this->getConnection(), '-al '.$this->directory.$directory) ?: array()
);
$fileData = $dirs = array();
foreach ($items as $itemData) {
if ('..' === $itemData['name'] || '.' === $itemData['name']) {
continue;
}
$item = array(
'name' => $itemData['name'],
'path' => trim(($directory ? $directory.'/' : '').$itemData['name'], '/'),
'time' => $itemData['time'],
'size' => $itemData['size'],
);
if ('-' === substr($itemData['perms'], 0, 1)) {
$fileData[$item['path']] = $item;
} elseif ('d' === substr($itemData['perms'], 0, 1)) {
$dirs[] = $item['path'];
}
}
$this->fileData = array_merge($fileData, $this->fileData);
return array(
'keys' => array_keys($fileData),
'dirs' => $dirs,
);
}
/**
* {@inheritdoc}
*/
public function createFile($key, Filesystem $filesystem)
{
$this->ensureDirectoryExists($this->directory, $this->create);
$file = new File($key, $filesystem);
if (!array_key_exists($key, $this->fileData)) {
$dirname = \Gaufrette\Util\Path::dirname($key);
$directory = $dirname == '.' ? '' : $dirname;
$this->listDirectory($directory);
}
if (isset($this->fileData[$key])) {
$fileData = $this->fileData[$key];
$file->setName($fileData['name']);
$file->setSize($fileData['size']);
}
return $file;
}
/**
* @param string $key
*
* @return int
*
* @throws \RuntimeException
*/
public function size($key)
{
$this->ensureDirectoryExists($this->directory, $this->create);
if (-1 === $size = ftp_size($this->connection, $key)) {
throw new \RuntimeException(sprintf('Unable to fetch the size of "%s".', $key));
}
return $size;
}
/**
* Ensures the specified directory exists. If it does not, and the create
* parameter is set to TRUE, it tries to create it.
*
* @param string $directory
* @param bool $create Whether to create the directory if it does not
* exist
*
* @throws RuntimeException if the directory does not exist and could not
* be created
*/
protected function ensureDirectoryExists($directory, $create = false)
{
if (!$this->isDir($directory)) {
if (!$create) {
throw new \RuntimeException(sprintf('The directory \'%s\' does not exist.', $directory));
}
$this->createDirectory($directory);
}
}
/**
* Creates the specified directory and its parent directories.
*
* @param string $directory Directory to create
*
* @throws RuntimeException if the directory could not be created
*/
protected function createDirectory($directory)
{
// create parent directory if needed
$parent = \Gaufrette\Util\Path::dirname($directory);
if (!$this->isDir($parent)) {
$this->createDirectory($parent);
}
// create the specified directory
$created = ftp_mkdir($this->getConnection(), $directory);
if (false === $created) {
throw new \RuntimeException(sprintf('Could not create the \'%s\' directory.', $directory));
}
}
/**
* @param string $directory - full directory path
*
* @return bool
*/
private function isDir($directory)
{
if ('/' === $directory) {
return true;
}
if (!@ftp_chdir($this->getConnection(), $directory)) {
return false;
}
// change directory again to return in the base directory
ftp_chdir($this->getConnection(), $this->directory);
return true;
}
private function fetchKeys($directory = '', $onlyKeys = true)
{
$directory = preg_replace('/^[\/]*([^\/].*)$/', '/$1', $directory);
$lines = ftp_rawlist($this->getConnection(), '-alR '.$this->directory.$directory);
if (false === $lines) {
return array('keys' => array(), 'dirs' => array());
}
$regexDir = '/'.preg_quote($this->directory.$directory, '/').'\/?(.+):$/u';
$regexItem = '/^(?:([d\-\d])\S+)\s+\S+(?:(?:\s+\S+){5})?\s+(\S+)\s+(.+?)$/';
$prevLine = null;
$directories = array();
$keys = array('keys' => array(), 'dirs' => array());
foreach ((array) $lines as $line) {
if ('' === $prevLine && preg_match($regexDir, $line, $match)) {
$directory = $match[1];
unset($directories[$directory]);
if ($onlyKeys) {
$keys = array(
'keys' => array_merge($keys['keys'], $keys['dirs']),
'dirs' => array(),
);
}
} elseif (preg_match($regexItem, $line, $tokens)) {
$name = $tokens[3];
if ('.' === $name || '..' === $name) {
continue;
}
$path = ltrim($directory.'/'.$name, '/');
if ('d' === $tokens[1] || '<dir>' === $tokens[2]) {
$keys['dirs'][] = $path;
$directories[$path] = true;
} else {
$keys['keys'][] = $path;
}
}
$prevLine = $line;
}
if ($onlyKeys) {
$keys = array(
'keys' => array_merge($keys['keys'], $keys['dirs']),
'dirs' => array(),
);
}
foreach (array_keys($directories) as $directory) {
$keys = array_merge_recursive($keys, $this->fetchKeys($directory, $onlyKeys));
}
return $keys;
}
/**
* Parses the given raw list.
*
* @param array $rawlist
*
* @return array
*/
private function parseRawlist(array $rawlist)
{
$parsed = array();
foreach ($rawlist as $line) {
$infos = preg_split("/[\s]+/", $line, 9);
if ($this->isLinuxListing($infos)) {
$infos[7] = (strrpos($infos[7], ':') != 2) ? ($infos[7].' 00:00') : (date('Y').' '.$infos[7]);
if ('total' !== $infos[0]) {
$parsed[] = array(
'perms' => $infos[0],
'num' => $infos[1],
'size' => $infos[4],
'time' => strtotime($infos[5].' '.$infos[6].'. '.$infos[7]),
'name' => $infos[8],
);
}
} elseif (count($infos) >= 4) {
$isDir = (boolean) ('<dir>' === $infos[2]);
$parsed[] = array(
'perms' => $isDir ? 'd' : '-',
'num' => '',
'size' => $isDir ? '' : $infos[2],
'time' => strtotime($infos[0].' '.$infos[1]),
'name' => $infos[3],
);
}
}
return $parsed;
}
/**
* Computes the path for the given key.
*
* @param string $key
*/
private function computePath($key)
{
return rtrim($this->directory, '/').'/'.$key;
}
/**
* Indicates whether the adapter has an open ftp connection.
*
* @return bool
*/
private function isConnected()
{
return is_resource($this->connection);
}
/**
* Returns an opened ftp connection resource. If the connection is not
* already opened, it open it before.
*
* @return resource The ftp connection
*/
private function getConnection()
{
if (!$this->isConnected()) {
$this->connect();
}
return $this->connection;
}
/**
* Opens the adapter's ftp connection.
*
* @throws RuntimeException if could not connect
*/
private function connect()
{
if ($this->ssl && !function_exists('ftp_ssl_connect')) {
throw new \RuntimeException('This Server Has No SSL-FTP Available.');
}
// open ftp connection
if (!$this->ssl) {
$this->connection = ftp_connect($this->host, $this->port, $this->timeout);
} else {
$this->connection = ftp_ssl_connect($this->host, $this->port, $this->timeout);
}
if (!$this->connection) {
throw new \RuntimeException(sprintf('Could not connect to \'%s\' (port: %s).', $this->host, $this->port));
}
if (defined('FTP_USEPASVADDRESS')) {
ftp_set_option($this->connection, FTP_USEPASVADDRESS, false);
}
$username = $this->username ?: 'anonymous';
$password = $this->password ?: '';
// login ftp user
if (!@ftp_login($this->connection, $username, $password)) {
$this->close();
throw new \RuntimeException(sprintf('Could not login as %s.', $username));
}
// switch to passive mode if needed
if ($this->passive && !ftp_pasv($this->connection, true)) {
$this->close();
throw new \RuntimeException('Could not turn passive mode on.');
}
// enable utf8 mode if configured
if($this->utf8 == true) {
ftp_raw($this->connection, "OPTS UTF8 ON");
}
// ensure the adapter's directory exists
if ('/' !== $this->directory) {
try {
$this->ensureDirectoryExists($this->directory, $this->create);
} catch (\RuntimeException $e) {
$this->close();
throw $e;
}
// change the current directory for the adapter's directory
if (!ftp_chdir($this->connection, $this->directory)) {
$this->close();
throw new \RuntimeException(sprintf('Could not change current directory for the \'%s\' directory.', $this->directory));
}
}
}
/**
* Closes the adapter's ftp connection.
*/
public function close()
{
if ($this->isConnected()) {
ftp_close($this->connection);
}
}
private function isLinuxListing($info)
{
return count($info) >= 9;
}
}

View File

@@ -0,0 +1,399 @@
<?php
namespace Gaufrette\Adapter;
use Gaufrette\Adapter;
use GuzzleHttp;
/**
* Google Cloud Storage adapter using the Google APIs Client Library for PHP.
*
* @author Patrik Karisch <patrik@karisch.guru>
*/
class GoogleCloudStorage implements Adapter,
MetadataSupporter,
ListKeysAware
{
protected $service;
protected $bucket;
protected $options;
protected $bucketExists;
protected $metadata = array();
protected $detectContentType;
/**
* @param \Google_Service_Storage $service The storage service class with authenticated
* client and full access scope
* @param string $bucket The bucket name
* @param array $options Options can be directory and acl
* @param bool $detectContentType Whether to detect the content type or not
*/
public function __construct(
\Google_Service_Storage $service,
$bucket,
array $options = array(),
$detectContentType = false
) {
$this->service = $service;
$this->bucket = $bucket;
$this->options = array_replace(
array(
'directory' => '',
'acl' => 'private',
),
$options
);
$this->detectContentType = $detectContentType;
}
/**
* @return array The actual options
*/
public function getOptions()
{
return $this->options;
}
/**
* @param array $options The new options
*/
public function setOptions($options)
{
$this->options = array_replace($this->options, $options);
}
/**
* @return string The current bucket name
*/
public function getBucket()
{
return $this->bucket;
}
/**
* Sets a new bucket name.
*
* @param string $bucket The new bucket name
*/
public function setBucket($bucket)
{
$this->bucket = $bucket;
}
/**
* {@inheritdoc}
*/
public function read($key)
{
$this->ensureBucketExists();
$path = $this->computePath($key);
$object = $this->getObjectData($path);
if ($object === false) {
return false;
}
if (class_exists('Google_Http_Request')) {
$request = new \Google_Http_Request($object->getMediaLink());
$this->service->getClient()->getAuth()->sign($request);
$response = $this->service->getClient()->getIo()->executeRequest($request);
if ($response[2] == 200) {
$this->setMetadata($key, $object->getMetadata());
return $response[0];
}
} else {
$httpClient = new GuzzleHttp\Client();
$httpClient = $this->service->getClient()->authorize($httpClient);
$response = $httpClient->request('GET', $object->getMediaLink());
if ($response->getStatusCode() == 200) {
$this->setMetadata($key, $object->getMetadata());
return $response->getBody();
}
}
return false;
}
/**
* {@inheritdoc}
*/
public function write($key, $content)
{
$this->ensureBucketExists();
$path = $this->computePath($key);
$metadata = $this->getMetadata($key);
$options = array(
'uploadType' => 'multipart',
'data' => $content,
);
/*
* If the ContentType was not already set in the metadata, then we autodetect
* it to prevent everything being served up as application/octet-stream.
*/
if (!isset($metadata['ContentType']) && $this->detectContentType) {
$options['mimeType'] = $this->guessContentType($content);
unset($metadata['ContentType']);
} elseif (isset($metadata['ContentType'])) {
$options['mimeType'] = $metadata['ContentType'];
unset($metadata['ContentType']);
}
$object = new \Google_Service_Storage_StorageObject();
$object->name = $path;
if (isset($metadata['ContentDisposition'])) {
$object->setContentDisposition($metadata['ContentDisposition']);
unset($metadata['ContentDisposition']);
}
if (isset($metadata['CacheControl'])) {
$object->setCacheControl($metadata['CacheControl']);
unset($metadata['CacheControl']);
}
if (isset($metadata['ContentLanguage'])) {
$object->setContentLanguage($metadata['ContentLanguage']);
unset($metadata['ContentLanguage']);
}
if (isset($metadata['ContentEncoding'])) {
$object->setContentEncoding($metadata['ContentEncoding']);
unset($metadata['ContentEncoding']);
}
$object->setMetadata($metadata);
try {
$object = $this->service->objects->insert($this->bucket, $object, $options);
if ($this->options['acl'] == 'public') {
$acl = new \Google_Service_Storage_ObjectAccessControl();
$acl->setEntity('allUsers');
$acl->setRole('READER');
$this->service->objectAccessControls->insert($this->bucket, $path, $acl);
}
return $object->getSize();
} catch (\Google_Service_Exception $e) {
return false;
}
}
/**
* {@inheritdoc}
*/
public function exists($key)
{
$this->ensureBucketExists();
$path = $this->computePath($key);
try {
$this->service->objects->get($this->bucket, $path);
} catch (\Google_Service_Exception $e) {
return false;
}
return true;
}
/**
* {@inheritdoc}
*/
public function keys()
{
return $this->listKeys();
}
/**
* {@inheritdoc}
*/
public function mtime($key)
{
$this->ensureBucketExists();
$path = $this->computePath($key);
$object = $this->getObjectData($path);
return $object ? strtotime($object->getUpdated()) : false;
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
$this->ensureBucketExists();
$path = $this->computePath($key);
try {
$this->service->objects->delete($this->bucket, $path);
} catch (\Google_Service_Exception $e) {
return false;
}
return true;
}
/**
* {@inheritdoc}
*/
public function rename($sourceKey, $targetKey)
{
$this->ensureBucketExists();
$sourcePath = $this->computePath($sourceKey);
$targetPath = $this->computePath($targetKey);
$object = $this->getObjectData($sourcePath);
if ($object === false) {
return false;
}
try {
$this->service->objects->copy($this->bucket, $sourcePath, $this->bucket, $targetPath, $object);
$this->delete($sourcePath);
} catch (\Google_Service_Exception $e) {
return false;
}
return true;
}
/**
* {@inheritdoc}
*/
public function isDirectory($key)
{
if ($this->exists($key.'/')) {
return true;
}
return false;
}
/**
* {@inheritdoc}
*/
public function listKeys($prefix = '')
{
$this->ensureBucketExists();
$options = array();
if ((string) $prefix != '') {
$options['prefix'] = $this->computePath($prefix);
} elseif (!empty($this->options['directory'])) {
$options['prefix'] = $this->options['directory'];
}
$list = $this->service->objects->listObjects($this->bucket, $options);
$keys = array();
// FIXME: Temporary workaround for google/google-api-php-client#375
$reflectionClass = new \ReflectionClass('Google_Service_Storage_Objects');
$reflectionProperty = $reflectionClass->getProperty('collection_key');
$reflectionProperty->setAccessible(true);
$reflectionProperty->setValue($list, 'items');
/** @var \Google_Service_Storage_StorageObject $object */
foreach ($list as $object) {
$keys[] = $object->name;
}
sort($keys);
return $keys;
}
/**
* {@inheritdoc}
*/
public function setMetadata($key, $content)
{
$path = $this->computePath($key);
$this->metadata[$path] = $content;
}
/**
* {@inheritdoc}
*/
public function getMetadata($key)
{
$path = $this->computePath($key);
return isset($this->metadata[$path]) ? $this->metadata[$path] : array();
}
/**
* Ensures the specified bucket exists.
*
* @throws \RuntimeException if the bucket does not exists
*/
protected function ensureBucketExists()
{
if ($this->bucketExists) {
return;
}
try {
$this->service->buckets->get($this->bucket);
$this->bucketExists = true;
return;
} catch (\Google_Service_Exception $e) {
$this->bucketExists = false;
throw new \RuntimeException(
sprintf(
'The configured bucket "%s" does not exist.',
$this->bucket
)
);
}
}
protected function computePath($key)
{
if (empty($this->options['directory'])) {
return $key;
}
return sprintf('%s/%s', $this->options['directory'], $key);
}
/**
* @param string $path
* @param array $options
*
* @return bool|\Google_Service_Storage_StorageObject
*/
private function getObjectData($path, $options = array())
{
try {
return $this->service->objects->get($this->bucket, $path, $options);
} catch (\Google_Service_Exception $e) {
return false;
}
}
/**
* @param string $content
*
* @return string
*/
private function guessContentType($content)
{
$fileInfo = new \finfo(FILEINFO_MIME_TYPE);
if (is_resource($content)) {
return $fileInfo->file(stream_get_meta_data($content)['uri']);
}
return $fileInfo->buffer($content);
}
}

View File

@@ -0,0 +1,208 @@
<?php
namespace Gaufrette\Adapter;
use Gaufrette\Adapter;
use MongoDB\BSON\Regex;
use MongoDB\GridFS\Bucket;
use MongoDB\GridFS\Exception\FileNotFoundException;
/**
* Adapter for the GridFS filesystem on MongoDB database.
*
* @author Tomi Saarinen <tomi.saarinen@rohea.com>
* @author Antoine Hérault <antoine.herault@gmail.com>
* @author Leszek Prabucki <leszek.prabucki@gmail.com>
*/
class GridFS implements Adapter,
ChecksumCalculator,
MetadataSupporter,
ListKeysAware
{
/** @var array */
private $metadata = [];
/** @var Bucket */
private $bucket;
/**
* @param Bucket $bucket
*/
public function __construct(Bucket $bucket)
{
$this->bucket = $bucket;
}
/**
* {@inheritdoc}
*/
public function read($key)
{
try {
$stream = $this->bucket->openDownloadStreamByName($key);
} catch (FileNotFoundException $e) {
return false;
}
try {
return stream_get_contents($stream);
} finally {
fclose($stream);
}
}
/**
* {@inheritdoc}
*/
public function write($key, $content)
{
$stream = $this->bucket->openUploadStream($key, ['metadata' => $this->getMetadata($key)]);
try {
return fwrite($stream, $content);
} finally {
fclose($stream);
}
return false;
}
/**
* {@inheritdoc}
*/
public function isDirectory($key)
{
return false;
}
/**
* {@inheritdoc}
*/
public function rename($sourceKey, $targetKey)
{
$metadata = $this->getMetadata($sourceKey);
$writable = $this->bucket->openUploadStream($targetKey, ['metadata' => $metadata]);
try {
$this->bucket->downloadToStreamByName($sourceKey, $writable);
$this->setMetadata($targetKey, $metadata);
$this->delete($sourceKey);
} catch (FileNotFoundException $e) {
return false;
} finally {
fclose($writable);
}
return true;
}
/**
* {@inheritdoc}
*/
public function exists($key)
{
return (boolean) $this->bucket->findOne(['filename' => $key]);
}
/**
* {@inheritdoc}
*/
public function keys()
{
$keys = [];
$cursor = $this->bucket->find([], ['projection' => ['filename' => 1]]);
foreach ($cursor as $file) {
$keys[] = $file['filename'];
}
return $keys;
}
/**
* {@inheritdoc}
*/
public function mtime($key)
{
$file = $this->bucket->findOne(['filename' => $key], ['projection' => ['uploadDate' => 1]]);
return $file ? (int) $file['uploadDate']->toDateTime()->format('U') : false;
}
/**
* {@inheritdoc}
*/
public function checksum($key)
{
$file = $this->bucket->findOne(['filename' => $key], ['projection' => ['md5' => 1]]);
return $file ? $file['md5'] : false;
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
if (null === $file = $this->bucket->findOne(['filename' => $key], ['projection' => ['_id' => 1]])) {
return false;
}
$this->bucket->delete($file['_id']);
return true;
}
/**
* {@inheritdoc}
*/
public function setMetadata($key, $metadata)
{
$this->metadata[$key] = $metadata;
}
/**
* {@inheritdoc}
*/
public function getMetadata($key)
{
if (isset($this->metadata[$key])) {
return $this->metadata[$key];
} else {
$meta = $this->bucket->findOne(['filename' => $key], ['projection' => ['metadata' => 1,'_id' => 0]]);
if ($meta === null) {
return array();
}
$this->metadata[$key] = iterator_to_array($meta['metadata']);
return $this->metadata[$key];
}
}
/**
* {@inheritdoc}
*/
public function listKeys($prefix = '')
{
$prefix = trim($prefix);
if ($prefix === '') {
return [
'dirs' => [],
'keys' => $this->keys(),
];
}
$regex = new Regex(sprintf('^%s', $prefix), '');
$files = $this->bucket->find(['filename' => $regex], ['projection' => ['filename' => 1]]);
$result = [
'dirs' => [],
'keys' => [],
];
foreach ($files as $file) {
$result['keys'][] = $file['filename'];
}
return $result;
}
}

View File

@@ -0,0 +1,151 @@
<?php
namespace Gaufrette\Adapter;
use Gaufrette\Adapter;
use Gaufrette\Util;
/**
* In memory adapter.
*
* Stores some files in memory for test purposes
*
* @author Antoine Hérault <antoine.herault@gmail.com>
*/
class InMemory implements Adapter,
MimeTypeProvider
{
protected $files = array();
/**
* @param array $files An array of files
*/
public function __construct(array $files = array())
{
$this->setFiles($files);
}
/**
* Defines the files.
*
* @param array $files An array of files
*/
public function setFiles(array $files)
{
$this->files = array();
foreach ($files as $key => $file) {
if (!is_array($file)) {
$file = array('content' => $file);
}
$file = array_merge(array(
'content' => null,
'mtime' => null,
), $file);
$this->setFile($key, $file['content'], $file['mtime']);
}
}
/**
* Defines a file.
*
* @param string $key The key
* @param string $content The content
* @param int $mtime The last modified time (automatically set to now if NULL)
*/
public function setFile($key, $content = null, $mtime = null)
{
if (null === $mtime) {
$mtime = time();
}
$this->files[$key] = array(
'content' => (string) $content,
'mtime' => (integer) $mtime,
);
}
/**
* {@inheritdoc}
*/
public function read($key)
{
return $this->files[$key]['content'];
}
/**
* {@inheritdoc}
*/
public function rename($sourceKey, $targetKey)
{
$content = $this->read($sourceKey);
$this->delete($sourceKey);
return (boolean) $this->write($targetKey, $content);
}
/**
* {@inheritdoc}
*/
public function write($key, $content, array $metadata = null)
{
$this->files[$key]['content'] = $content;
$this->files[$key]['mtime'] = time();
return Util\Size::fromContent($content);
}
/**
* {@inheritdoc}
*/
public function exists($key)
{
return array_key_exists($key, $this->files);
}
/**
* {@inheritdoc}
*/
public function keys()
{
return array_keys($this->files);
}
/**
* {@inheritdoc}
*/
public function mtime($key)
{
return isset($this->files[$key]['mtime']) ? $this->files[$key]['mtime'] : false;
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
unset($this->files[$key]);
clearstatcache();
return true;
}
/**
* {@inheritdoc}
*/
public function isDirectory($path)
{
return false;
}
/**
* {@inheritdoc}
*/
public function mimeType($key)
{
$fileInfo = new \finfo(FILEINFO_MIME_TYPE);
return $fileInfo->buffer($this->files[$key]['content']);
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Gaufrette\Adapter;
use Gaufrette\Adapter\OpenStackCloudFiles\ObjectStoreFactoryInterface;
@trigger_error('The '.__NAMESPACE__.'\LazyOpenCloud adapter is deprecated since version 0.4 and will be removed in 1.0. Use the OpenCloud adapter instead.', E_USER_DEPRECATED);
/**
* LazyOpenCloud.
*
* @author Daniel Richter <nexyz9@gmail.com>
*
* @deprecated The LazyOpenCloud adapter is deprecated since version 0.4 and will be removed in 1.0. Use the OpenCloud adapter instead.
*/
class LazyOpenCloud extends OpenCloud
{
/**
* @var ObjectStoreFactoryInterface
*/
protected $objectStoreFactory;
/**
* @param ObjectStoreFactoryInterface $objectStoreFactory
* @param string $containerName
* @param bool $createContainer
*/
public function __construct(ObjectStoreFactoryInterface $objectStoreFactory, $containerName, $createContainer = false)
{
$this->objectStoreFactory = $objectStoreFactory;
$this->containerName = $containerName;
$this->createContainer = $createContainer;
}
/**
* Override parent to lazy-load object store.
*
* {@inheritdoc}
*/
protected function getContainer()
{
if (!$this->objectStore) {
$this->objectStore = $this->objectStoreFactory->getObjectStore();
}
return parent::getContainer();
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Gaufrette\Adapter;
/**
* interface that adds support of native listKeys to adapter.
*
* @author Andrew Tch <andrew.tchircoff@gmail.com>
*/
interface ListKeysAware
{
/**
* Lists keys beginning with pattern given
* (no wildcard / regex matching).
*
* @param string $prefix
*
* @return array
*/
public function listKeys($prefix = '');
}

View File

@@ -0,0 +1,312 @@
<?php
namespace Gaufrette\Adapter;
use Gaufrette\Util;
use Gaufrette\Adapter;
use Gaufrette\Stream;
/**
* Adapter for the local filesystem.
*
* @author Antoine Hérault <antoine.herault@gmail.com>
* @author Leszek Prabucki <leszek.prabucki@gmail.com>
*/
class Local implements Adapter,
StreamFactory,
ChecksumCalculator,
SizeCalculator,
MimeTypeProvider
{
protected $directory;
private $create;
private $mode;
/**
* @param string $directory Directory where the filesystem is located
* @param bool $create Whether to create the directory if it does not
* exist (default FALSE)
* @param int $mode Mode for mkdir
*
* @throws \RuntimeException if the specified directory does not exist and
* could not be created
*/
public function __construct($directory, $create = false, $mode = 0777)
{
$this->directory = Util\Path::normalize($directory);
if (is_link($this->directory)) {
$this->directory = realpath($this->directory);
}
$this->create = $create;
$this->mode = $mode;
}
/**
* {@inheritdoc}
*
* @throws \OutOfBoundsException If the computed path is out of the directory
* @throws \InvalidArgumentException if the directory already exists
* @throws \RuntimeException if the directory could not be created
*/
public function read($key)
{
return file_get_contents($this->computePath($key));
}
/**
* {@inheritdoc}
*
* @throws \OutOfBoundsException If the computed path is out of the directory
* @throws \InvalidArgumentException if the directory already exists
* @throws \RuntimeException if the directory could not be created
*/
public function write($key, $content)
{
$path = $this->computePath($key);
$this->ensureDirectoryExists(\Gaufrette\Util\Path::dirname($path), true);
return file_put_contents($path, $content);
}
/**
* {@inheritdoc}
*
* @throws \OutOfBoundsException If the computed path is out of the directory
* @throws \InvalidArgumentException if the directory already exists
* @throws \RuntimeException if the directory could not be created
*/
public function rename($sourceKey, $targetKey)
{
$targetPath = $this->computePath($targetKey);
$this->ensureDirectoryExists(\Gaufrette\Util\Path::dirname($targetPath), true);
return rename($this->computePath($sourceKey), $targetPath);
}
/**
* {@inheritdoc}
*/
public function exists($key)
{
return is_file($this->computePath($key));
}
/**
* {@inheritdoc}
*
* @throws \OutOfBoundsException If the computed path is out of the directory
* @throws \InvalidArgumentException if the directory already exists
* @throws \RuntimeException if the directory could not be created
*/
public function keys()
{
$this->ensureDirectoryExists($this->directory, $this->create);
try {
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator(
$this->directory,
\FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS
),
\RecursiveIteratorIterator::CHILD_FIRST
);
} catch (\Exception $e) {
$files = new \EmptyIterator();
}
$keys = [];
foreach ($files as $file) {
$keys[] = $this->computeKey($file);
}
sort($keys);
return $keys;
}
/**
* {@inheritdoc}
*
* @throws \OutOfBoundsException If the computed path is out of the directory
* @throws \InvalidArgumentException if the directory already exists
* @throws \RuntimeException if the directory could not be created
*/
public function mtime($key)
{
return filemtime($this->computePath($key));
}
/**
* {@inheritdoc}
*
* @throws \OutOfBoundsException If the computed path is out of the directory
* @throws \InvalidArgumentException if the directory already exists
* @throws \RuntimeException if the directory could not be created
*/
public function delete($key)
{
if ($this->isDirectory($key)) {
return rmdir($this->computePath($key));
}
return unlink($this->computePath($key));
}
/**
* @param string $key
*
* @return bool
*
* @throws \OutOfBoundsException If the computed path is out of the directory
* @throws \InvalidArgumentException if the directory already exists
* @throws \RuntimeException if the directory could not be created
*/
public function isDirectory($key)
{
return is_dir($this->computePath($key));
}
/**
* {@inheritdoc}
*
* @throws \OutOfBoundsException If the computed path is out of the directory
* @throws \InvalidArgumentException if the directory already exists
* @throws \RuntimeException if the directory could not be created
*/
public function createStream($key)
{
return new Stream\Local($this->computePath($key), $this->mode);
}
/**
* {@inheritdoc}
*
* @throws \OutOfBoundsException If the computed path is out of the directory
* @throws \InvalidArgumentException if the directory already exists
* @throws \RuntimeException if the directory could not be created
*/
public function checksum($key)
{
return Util\Checksum::fromFile($this->computePath($key));
}
/**
* {@inheritdoc}
*
* @throws \OutOfBoundsException If the computed path is out of the directory
* @throws \InvalidArgumentException if the directory already exists
* @throws \RuntimeException if the directory could not be created
*/
public function size($key)
{
return Util\Size::fromFile($this->computePath($key));
}
/**
* {@inheritdoc}
*
* @throws \OutOfBoundsException If the computed path is out of the directory
* @throws \InvalidArgumentException if the directory already exists
* @throws \RuntimeException if the directory could not be created
*/
public function mimeType($key)
{
$fileInfo = new \finfo(FILEINFO_MIME_TYPE);
return $fileInfo->file($this->computePath($key));
}
/**
* Computes the key from the specified path.
*
* @param $path
* @return string
*
* @throws \OutOfBoundsException If the computed path is out of the directory
* @throws \InvalidArgumentException if the directory already exists
* @throws \RuntimeException if the directory could not be created
*/
public function computeKey($path)
{
$path = $this->normalizePath($path);
return ltrim(substr($path, strlen($this->directory)), '/');
}
/**
* Computes the path from the specified key.
*
* @param string $key The key which for to compute the path
*
* @return string A path
*
* @throws \InvalidArgumentException If the directory already exists
* @throws \OutOfBoundsException If the computed path is out of the directory
* @throws \RuntimeException If directory does not exists and cannot be created
*/
protected function computePath($key)
{
$this->ensureDirectoryExists($this->directory, $this->create);
return $this->normalizePath($this->directory.'/'.$key);
}
/**
* Normalizes the given path.
*
* @param string $path
*
* @return string
* @throws \OutOfBoundsException If the computed path is out of the
* directory
*/
protected function normalizePath($path)
{
$path = Util\Path::normalize($path);
if (0 !== strpos($path, $this->directory)) {
throw new \OutOfBoundsException(sprintf('The path "%s" is out of the filesystem.', $path));
}
return $path;
}
/**
* Ensures the specified directory exists, creates it if it does not.
*
* @param string $directory Path of the directory to test
* @param bool $create Whether to create the directory if it does
* not exist
*
* @throws \InvalidArgumentException if the directory already exists
* @throws \RuntimeException if the directory does not exists and could not
* be created
*/
protected function ensureDirectoryExists($directory, $create = false)
{
if (!is_dir($directory)) {
if (!$create) {
throw new \RuntimeException(sprintf('The directory "%s" does not exist.', $directory));
}
$this->createDirectory($directory);
}
}
/**
* Creates the specified directory and its parents.
*
* @param string $directory Path of the directory to create
*
* @throws \InvalidArgumentException if the directory already exists
* @throws \RuntimeException if the directory could not be created
*/
protected function createDirectory($directory)
{
if (!@mkdir($directory, $this->mode, true) && !is_dir($directory)) {
throw new \RuntimeException(sprintf('The directory \'%s\' could not be created.', $directory));
}
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Gaufrette\Adapter;
/**
* Interface which add supports for metadata.
*
* @author Leszek Prabucki <leszek.prabucki@gmail.com>
*/
interface MetadataSupporter
{
/**
* @param string $key
* @param array $content
*/
public function setMetadata($key, $content);
/**
* @param string $key
*
* @return array
*/
public function getMetadata($key);
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Gaufrette\Adapter;
/**
* Interface which add mime type provider support to adapter.
*
* @author Gildas Quemener <gildas.quemener@gmail.com>
*/
interface MimeTypeProvider
{
/**
* Returns the mime type of the specified key.
*
* @param string $key
*
* @return string
*/
public function mimeType($key);
}

View File

@@ -0,0 +1,372 @@
<?php
namespace Gaufrette\Adapter;
use Gaufrette\Adapter;
use Gaufrette\Util;
@trigger_error('The '.__NAMESPACE__.'\MogileFS adapter is deprecated since version 0.4 and will be removed in 1.0.', E_USER_DEPRECATED);
/**
* Adapter for the MogileFS filesystem.
*
* @author Mikko Tarvainen 2011 <mtarvainen@gmail.com>
* @author Antoine Hérault <antoine.herault@gmail.com>
*
* @deprecated The MogileFS adapter is deprecated since version 0.4 and will be removed in 1.0.
*
* Bases partly on Wikimedia MogileFS client code by Jens Frank and Domas Mituzas, 2007.
* See more: http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/MogileClient/
*/
class MogileFS implements Adapter
{
const ERR_OTHER = 0;
const ERR_UNKNOWN_KEY = 1;
const ERR_EMPTY_FILE = 2;
const ERR_NONE_MATCH = 3;
const ERR_KEY_EXISTS = 4;
protected $domain;
protected $hosts;
protected $socket;
/**
* @param domain MogileFS domain
* @param hosts Array of MogileFS trackers
*/
public function __construct($domain, array $hosts)
{
if (strlen($domain) < 1 || count($hosts) < 1) {
throw new \InvalidArgumentException('Invalid parameters. Given domain is too short or you not given any host.');
}
$this->domain = $domain;
$this->hosts = $hosts;
}
/**
* {@inheritdoc}
*/
public function read($key)
{
$paths = $this->getPaths($key);
$data = '';
if ($paths) {
shuffle($paths);
foreach ($paths as $path) {
$fh = fopen($path, 'r');
if (!$fh) {
continue;
}
while (!feof($fh)) {
$data .= fread($fh, 8192);
}
fclose($fh);
break;
}
}
return $data ?: false;
}
/**
* {@inheritdoc}
*/
public function write($key, $content, array $metadata = null)
{
$closeres = false;
if (mb_strlen($content) > 0) {
$res = $this->doRequest('CREATE_OPEN', array('key' => $key, 'class' => $metadata['mogile_class']));
if ($res && preg_match('/^http:\/\/([a-z0-9.-]*):([0-9]*)\/(.*)$/', $res['path'], $matches)) {
$host = $matches[1];
$port = $matches[2];
$path = $matches[3];
$status = $this->putFile($res['path'], $content);
if ($status) {
$params = array('key' => $key, 'class' => $metadata['mogile_class'], 'devid' => $res['devid'],
'fid' => $res['fid'], 'path' => urldecode($res['path']), );
$closeres = $this->doRequest('CREATE_CLOSE', $params);
}
}
}
if (!is_array($closeres)) {
return false;
}
return Util\Size::fromContent($content);
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
$this->doRequest('DELETE', array('key' => $key));
return true;
}
/**
* {@inheritdoc}
*/
public function rename($sourceKey, $targetKey)
{
$this->doRequest('RENAME', array(
'from_key' => $sourceKey,
'to_key' => $targetKey,
));
return true;
}
/**
* {@inheritdoc}
*/
public function exists($key)
{
try {
$this->getPaths($key);
} catch (\RuntimeException $e) {
return false;
}
return true;
}
/**
* {@inheritdoc}
*/
public function keys()
{
try {
$result = $this->doRequest('LIST_KEYS');
} catch (\RuntimeException $e) {
if (self::ERR_NONE_MATCH === $e->getCode()) {
return array();
}
throw $e;
}
unset($result['key_count'], $result['next_after']);
return array_values($result);
}
/**
* {@inheritdoc}
*/
public function mtime($key)
{
return false;
}
/**
* {@inheritdoc}
*/
public function isDirectory($key)
{
return false;
}
/**
* Get available domains and classes from tracker.
*
* @return mixed Array on success, false on failure
*/
public function getDomains()
{
$res = $this->doRequest('GET_DOMAINS');
if (!$res) {
return false;
}
$domains = array();
for ($i = 1; $i <= $res['domains']; ++$i) {
$dom = 'domain'.$i;
$classes = array();
// Associate classes to current domain (class name => mindevcount)
for ($j = 1; $j <= $res[$dom.'classes']; ++$j) {
$classes[$res[$dom.'class'.$j.'name']] = $res[$dom.'class'.$j.'mindevcount'];
}
$domains[] = array('name' => $res[$dom], 'classes' => $classes);
}
return $domains;
}
/**
* Tries to connect MogileFS tracker.
*
* @return Socket
*/
private function connect()
{
if ($this->socket) {
return $this->socket;
}
shuffle($this->hosts);
foreach ($this->hosts as $host) {
list($ip, $port) = explode(':', $host);
$this->socket = @fsockopen($ip, $port, $err_no, $err_str, 1);
if ($this->socket) {
break;
}
}
if (!$this->socket) {
throw new \RuntimeException('Unable to connect to the tracker.');
}
return $this->socket;
}
/**
* Close connection to MogileFS tracker.
*
* @return bool
*/
private function close()
{
if ($this->socket) {
return fclose($this->socket);
}
return true;
}
/**
* Makes request to MogileFS tracker.
*
* @param cmd Command
* @param args Array of arguments
*
* @return mixed Array on success, false on failure
*/
private function doRequest($cmd, $args = array())
{
clearstatcache();
$args['domain'] = $this->domain;
$params = http_build_query($args);
$this->connect();
fwrite($this->socket, "{$cmd} {$params}\n");
$line = fgets($this->socket);
$words = explode(' ', $line);
if ($words[0] == 'OK') {
parse_str(trim($words[1]), $result);
} else {
$errorName = empty($words[1]) ? null : $words[1];
switch ($errorName) {
case 'unknown_key':
$errorCode = static::ERR_UNKNOWN_KEY;
break;
case 'empty_file':
$errorCode = static::ERR_EMPTY_FILE;
break;
case 'none_match':
$errorCode = static::ERR_NONE_MATCH;
break;
case 'key_exists':
$errorCode = static::ERR_KEY_EXISTS;
break;
default:
$errorCode = static::ERR_OTHER;
}
throw new \RuntimeException(
sprintf('Error response: %s', $line),
$errorCode
);
}
return $result;
}
/**
* Get file location at server from MogileFS tracker.
*
* @param key File key
*
* @return mixed Array on success, false on failure
*/
private function getPaths($key)
{
$res = $this->doRequest('GET_PATHS', array('key' => $key));
unset($res['paths']);
return $res;
}
/**
* Sends file to MogileFS tracker.
*
* @param path Save path at server
* @param data Data to save
*
* @return bool
*/
private function putFile($path, $data)
{
$info = false;
$url = parse_url($path);
$fp = fsockopen($url['host'], $url['port'], $errno, $errstr, 5);
if (!$fp) {
return false;
}
$buffer = '';
$b = "\r\n";
stream_set_blocking($fp, true);
stream_set_timeout($fp, 30, 200000);
$out = 'PUT '.$url['path'].' HTTP/1.1'.$b;
$out .= 'Host: '.$url['host'].$b;
$out .= 'Content-Length: '.Util\Size::fromContent($data).$b.$b;
$out .= $data;
$out .= $b.$b;
fwrite($fp, $out);
fflush($fp);
stream_set_blocking($fp, true);
stream_set_timeout($fp, 30, 200000);
while (!feof($fp) && !$info['timed_out']) {
$info = stream_get_meta_data($fp);
$buffer .= fgets($fp, 128);
}
fclose($fp);
return true;
}
/**
* Closes the underlying connection.
*/
public function __destruct()
{
$this->close();
}
}

View File

@@ -0,0 +1,253 @@
<?php
namespace Gaufrette\Adapter;
use Gaufrette\Adapter;
use Gaufrette\Util;
use Guzzle\Http\Exception\BadResponseException;
use OpenCloud\Common\Exceptions\DeleteError;
use OpenCloud\ObjectStore\Resource\Container;
use OpenCloud\ObjectStore\Service;
use OpenCloud\Common\Exceptions\CreateUpdateError;
use OpenCloud\ObjectStore\Exception\ObjectNotFoundException;
/**
* OpenCloud adapter.
*
* @author James Watson <james@sitepulse.org>
* @author Daniel Richter <nexyz9@gmail.com>
*/
class OpenCloud implements Adapter,
ChecksumCalculator
{
/**
* @var Service
*/
protected $objectStore;
/**
* @var string
*/
protected $containerName;
/**
* @var bool
*/
protected $createContainer;
/**
* @var Container
*/
protected $container;
/**
* @param Service $objectStore
* @param string $containerName The name of the container
* @param bool $createContainer Whether to create the container if it does not exist
*/
public function __construct(Service $objectStore, $containerName, $createContainer = false)
{
$this->objectStore = $objectStore;
$this->containerName = $containerName;
$this->createContainer = $createContainer;
}
/**
* Returns an initialized container.
*
* @throws \RuntimeException
*
* @return Container
*/
protected function getContainer()
{
if ($this->container) {
return $this->container;
}
try {
return $this->container = $this->objectStore->getContainer($this->containerName);
} catch (BadResponseException $e) { //OpenCloud lib does not wrap this exception
if (!$this->createContainer) {
throw new \RuntimeException(sprintf('Container "%s" does not exist.', $this->containerName));
}
}
if (!$container = $this->objectStore->createContainer($this->containerName)) {
throw new \RuntimeException(sprintf('Container "%s" could not be created.', $this->containerName));
}
return $this->container = $container;
}
/**
* Reads the content of the file.
*
* @param string $key
*
* @return string|bool if cannot read content
*/
public function read($key)
{
if ($object = $this->tryGetObject($key)) {
return $object->getContent();
}
return false;
}
/**
* Writes the given content into the file.
*
* @param string $key
* @param string $content
*
* @return int|bool The number of bytes that were written into the file
*/
public function write($key, $content)
{
try {
$this->getContainer()->uploadObject($key, $content);
} catch (CreateUpdateError $updateError) {
return false;
}
return Util\Size::fromContent($content);
}
/**
* Indicates whether the file exists.
*
* @param string $key
*
* @return bool
*/
public function exists($key)
{
try {
$exists = $this->getContainer()->getPartialObject($key) !== false;
} catch (BadResponseException $objFetchError) {
return false;
}
return $exists;
}
/**
* Returns an array of all keys (files and directories).
*
* @return array
*/
public function keys()
{
$objectList = $this->getContainer()->objectList();
$keys = array();
while ($object = $objectList->next()) {
$keys[] = $object->getName();
}
sort($keys);
return $keys;
}
/**
* Returns the last modified time.
*
* @param string $key
*
* @return int|bool An UNIX like timestamp or false
*/
public function mtime($key)
{
if ($object = $this->tryGetObject($key)) {
return (new \DateTime($object->getLastModified()))->format('U');
}
return false;
}
/**
* Deletes the file.
*
* @param string $key
*
* @return bool
*/
public function delete($key)
{
if (!$object = $this->tryGetObject($key)) {
return false;
}
try {
$object->delete();
} catch (DeleteError $deleteError) {
return false;
}
return true;
}
/**
* Renames a file.
*
* @param string $sourceKey
* @param string $targetKey
*
* @return bool
*/
public function rename($sourceKey, $targetKey)
{
if (false !== $this->write($targetKey, $this->read($sourceKey))) {
$this->delete($sourceKey);
return true;
}
return false;
}
/**
* Check if key is directory.
*
* @param string $key
*
* @return bool
*/
public function isDirectory($key)
{
return false;
}
/**
* Returns the checksum of the specified key.
*
* @param string $key
*
* @return string
*/
public function checksum($key)
{
if ($object = $this->tryGetObject($key)) {
return $object->getETag();
}
return false;
}
/**
* @param string $key
*
* @return \OpenCloud\ObjectStore\Resource\DataObject|false
*/
protected function tryGetObject($key)
{
try {
return $this->getContainer()->getObject($key);
} catch (ObjectNotFoundException $objFetchError) {
return false;
}
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace Gaufrette\Adapter\OpenStackCloudFiles;
use OpenCloud\OpenStack;
/**
* ObjectStoreFactory.
*
* @author Daniel Richter <nexyz9@gmail.com>
*/
class ObjectStoreFactory implements ObjectStoreFactoryInterface
{
/**
* @var OpenStack
*/
protected $connection;
/**
* @var string
*/
protected $region;
/**
* @var string
*/
protected $urlType;
/**
* @var string
*/
protected $objectStoreType;
/**
* @param OpenStack $connection
* @param string $region
* @param string $urlType
* @param string $objectStoreType
*/
public function __construct(OpenStack $connection, $region, $urlType, $objectStoreType = 'cloudFiles')
{
$this->connection = $connection;
$this->region = $region;
$this->urlType = $urlType;
$this->objectStoreType = $objectStoreType;
}
/**
* {@inheritdoc}
*/
public function getObjectStore()
{
return $this->connection->objectStoreService($this->objectStoreType, $this->region, $this->urlType);
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Gaufrette\Adapter\OpenStackCloudFiles;
use OpenCloud\ObjectStore\Service;
/**
* ObjectStoreFactoryInterface.
*
* @author Daniel Richter <nexyz9@gmail.com>
*/
interface ObjectStoreFactoryInterface
{
/**
* @return Service
*/
public function getObjectStore();
}

View File

@@ -0,0 +1,241 @@
<?php
namespace Gaufrette\Adapter;
use Gaufrette\Adapter;
use phpseclib\Net\SFTP as SecLibSFTP;
use Gaufrette\Filesystem;
use Gaufrette\File;
class PhpseclibSftp implements Adapter,
FileFactory,
ListKeysAware
{
protected $sftp;
protected $directory;
protected $create;
protected $initialized = false;
/**
* @param SecLibSFTP $sftp An Sftp instance
* @param string $directory The distant directory
* @param bool $create Whether to create the remote directory if it
* does not exist
*/
public function __construct(SecLibSFTP $sftp, $directory = null, $create = false)
{
$this->sftp = $sftp;
$this->directory = $directory;
$this->create = $create;
}
/**
* {@inheritdoc}
*/
public function read($key)
{
return $this->sftp->get($this->computePath($key));
}
/**
* {@inheritdoc}
*/
public function rename($sourceKey, $targetKey)
{
$this->initialize();
$sourcePath = $this->computePath($sourceKey);
$targetPath = $this->computePath($targetKey);
$this->ensureDirectoryExists(\Gaufrette\Util\Path::dirname($targetPath), true);
return $this->sftp->rename($sourcePath, $targetPath);
}
/**
* {@inheritdoc}
*/
public function write($key, $content)
{
$this->initialize();
$path = $this->computePath($key);
$this->ensureDirectoryExists(\Gaufrette\Util\Path::dirname($path), true);
if ($this->sftp->put($path, $content)) {
return $this->sftp->size($path);
}
return false;
}
/**
* {@inheritdoc}
*/
public function exists($key)
{
$this->initialize();
return false !== $this->sftp->stat($this->computePath($key));
}
/**
* {@inheritdoc}
*/
public function isDirectory($key)
{
$this->initialize();
$pwd = $this->sftp->pwd();
if ($this->sftp->chdir($this->computePath($key))) {
$this->sftp->chdir($pwd);
return true;
}
return false;
}
/**
* {@inheritdoc}
*/
public function keys()
{
$keys = $this->fetchKeys();
return $keys['keys'];
}
/**
* {@inheritdoc}
*/
public function listKeys($prefix = '')
{
preg_match('/(.*?)[^\/]*$/', $prefix, $match);
$directory = rtrim($match[1], '/');
$keys = $this->fetchKeys($directory, false);
if ($directory === $prefix) {
return $keys;
}
$filteredKeys = array();
foreach (array('keys', 'dirs') as $hash) {
$filteredKeys[$hash] = array();
foreach ($keys[$hash] as $key) {
if (0 === strpos($key, $prefix)) {
$filteredKeys[$hash][] = $key;
}
}
}
return $filteredKeys;
}
/**
* {@inheritdoc}
*/
public function mtime($key)
{
$this->initialize();
$stat = $this->sftp->stat($this->computePath($key));
return isset($stat['mtime']) ? $stat['mtime'] : false;
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
return $this->sftp->delete($this->computePath($key), false);
}
/**
* {@inheritdoc}
*/
public function createFile($key, Filesystem $filesystem)
{
$file = new File($key, $filesystem);
$stat = $this->sftp->stat($this->computePath($key));
if (isset($stat['size'])) {
$file->setSize($stat['size']);
}
return $file;
}
/**
* Performs the adapter's initialization.
*
* It will ensure the root directory exists
*/
protected function initialize()
{
if ($this->initialized) {
return;
}
$this->ensureDirectoryExists($this->directory, $this->create);
$this->initialized = true;
}
protected function ensureDirectoryExists($directory, $create)
{
$pwd = $this->sftp->pwd();
if ($this->sftp->chdir($directory)) {
$this->sftp->chdir($pwd);
} elseif ($create) {
if (!$this->sftp->mkdir($directory, 0777, true)) {
throw new \RuntimeException(sprintf('The directory \'%s\' does not exist and could not be created (%s).', $this->directory, $this->sftp->getLastSFTPError()));
}
} else {
throw new \RuntimeException(sprintf('The directory \'%s\' does not exist.', $this->directory));
}
}
protected function computePath($key)
{
return $this->directory.'/'.ltrim($key, '/');
}
protected function fetchKeys($directory = '', $onlyKeys = true)
{
$keys = array('keys' => array(), 'dirs' => array());
$computedPath = $this->computePath($directory);
if (!$this->sftp->file_exists($computedPath)) {
return $keys;
}
$list = $this->sftp->rawlist($computedPath);
foreach ((array) $list as $filename => $stat) {
if ('.' === $filename || '..' === $filename) {
continue;
}
$path = ltrim($directory.'/'.$filename, '/');
if (isset($stat['type']) && $stat['type'] === NET_SFTP_TYPE_DIRECTORY) {
$keys['dirs'][] = $path;
} else {
$keys['keys'][] = $path;
}
}
$dirs = $keys['dirs'];
if ($onlyKeys && !empty($dirs)) {
$keys['keys'] = array_merge($keys['keys'], $dirs);
$keys['dirs'] = array();
}
foreach ($dirs as $dir) {
$keys = array_merge_recursive($keys, $this->fetchKeys($dir, $onlyKeys));
}
return $keys;
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Gaufrette\Adapter;
/**
* Safe local adapter that encodes key to avoid the use of the directories
* structure.
*
* @author Antoine Hérault <antoine.herault@gmail.com>
*/
class SafeLocal extends Local
{
/**
* {@inheritdoc}
*/
public function computeKey($path)
{
return base64_decode(parent::computeKey($path));
}
/**
* {@inheritdoc}
*/
protected function computePath($key)
{
return parent::computePath(base64_encode($key));
}
}

View File

@@ -0,0 +1,232 @@
<?php
namespace Gaufrette\Adapter;
use Gaufrette\Adapter;
use Ssh\Sftp as SftpClient;
@trigger_error('The '.__NAMESPACE__.'\Sftp adapter is deprecated since version 0.4 and will be removed in 1.0.', E_USER_DEPRECATED);
/**
* @deprecated The Sftp adapter is deprecated since version 0.4 and will be removed in 1.0.
*/
class Sftp implements Adapter,
ChecksumCalculator
{
protected $sftp;
protected $directory;
protected $create;
protected $initialized = false;
/**
* @param \Ssh\Sftp $sftp An Sftp instance
* @param string $directory The distant directory
* @param bool $create Whether to create the remote directory if it
* does not exist
*/
public function __construct(SftpClient $sftp, $directory = null, $create = false)
{
$this->sftp = $sftp;
$this->directory = $directory;
$this->create = $create;
}
/**
* {@inheritdoc}
*/
public function read($key)
{
$this->initialize();
$content = $this->sftp->read($this->computePath($key));
return $content;
}
/**
* {@inheritdoc}
*/
public function rename($sourceKey, $targetKey)
{
$sourcePath = $this->computePath($sourceKey);
$targetPath = $this->computePath($targetKey);
$this->ensureDirectoryExists(\Gaufrette\Util\Path::dirname($targetPath), true);
return $this->sftp->rename($sourcePath, $targetPath);
}
/**
* {@inheritdoc}
*/
public function write($key, $content)
{
$this->initialize();
$path = $this->computePath($key);
$this->ensureDirectoryExists(\Gaufrette\Util\Path::dirname($path), true);
$numBytes = $this->sftp->write($path, $content);
return $numBytes;
}
/**
* {@inheritdoc}
*/
public function exists($key)
{
$this->initialize();
$url = $this->sftp->getUrl($this->computePath($key));
clearstatcache();
return file_exists($url);
}
/**
* {@inheritdoc}
*/
public function isDirectory($key)
{
$this->initialize();
$url = $this->sftp->getUrl($this->computePath($key));
return is_dir($url);
}
/**
* {@inheritdoc}
*/
public function keys()
{
$this->initialize();
$results = $this->sftp->listDirectory($this->directory, true);
$files = array_map(array($this, 'computeKey'), $results['files']);
$dirs = array();
foreach ($files as $file) {
if ('.' !== $dirname = \Gaufrette\Util\Path::dirname($file)) {
$dirs[] = $dirname;
}
}
$keys = array_merge($files, $dirs);
sort($keys);
return $keys;
}
/**
* {@inheritdoc}
*/
public function mtime($key)
{
$this->initialize();
return filemtime($this->sftp->getUrl($this->computePath($key)));
}
/**
* {@inheritdoc}
*/
public function checksum($key)
{
$this->initialize();
if ($this->exists($key)) {
return md5_file($this->sftp->getUrl($this->computePath($key)));
}
return false;
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
$this->initialize();
return unlink($this->sftp->getUrl($this->computePath($key)));
}
/**
* Computes the key from the specified path.
*
* @param string $path
*
* @return string
*/
public function computeKey($path)
{
if (0 !== strpos($path, $this->directory)) {
throw new \OutOfBoundsException(sprintf('The path \'%s\' is out of the filesystem.', $path));
}
return ltrim(substr($path, strlen($this->directory)), '/');
}
/**
* Computes the path for the specified key.
*
* @param string $key
*
* @return string
*/
protected function computePath($key)
{
return $this->directory.'/'.ltrim($key, '/');
}
/**
* Performs the adapter's initialization.
*
* It will ensure the root directory exists
*/
protected function initialize()
{
if ($this->initialized) {
return;
}
$this->ensureDirectoryExists($this->directory, $this->create);
$this->initialized = true;
}
/**
* Ensures the specified directory exists.
*
* @param string $directory The directory that we ensure the existence
* @param bool $create Whether to create it if it does not exist
*
* @throws RuntimeException if the specified directory does not exist and
* could not be created
*/
protected function ensureDirectoryExists($directory, $create = false)
{
$url = $this->sftp->getUrl($directory);
$resource = @opendir($url);
if (false === $resource && (!$create || !$this->createDirectory($directory))) {
throw new \RuntimeException(sprintf('The directory \'%s\' does not exist and could not be created.', $directory));
}
// make sure we don't leak the resource
if (is_resource($resource)) {
closedir($resource);
}
}
/**
* Creates the specified directory and its parents.
*
* @param string $directory The directory to create
*
* @return bool TRUE on success, or FALSE on failure
*/
protected function createDirectory($directory)
{
return $this->sftp->mkdir($directory, 0777, true);
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Gaufrette\Adapter;
/**
* Interface which add size calculation support to adapter.
*
* @author Markus Poerschke <markus@eluceo.de>
*/
interface SizeCalculator
{
/**
* Returns the size of the specified key.
*
* @param string $key
*
* @return int
*/
public function size($key);
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Gaufrette\Adapter;
/**
* Interface for the stream creation class.
*
* @author Leszek Prabucki <leszek.prabucki@gmail.com>
*/
interface StreamFactory
{
/**
* Creates a new stream instance of the specified file.
*
* @param string $key
*
* @return \Gaufrette\Stream
*/
public function createStream($key);
}

View File

@@ -0,0 +1,224 @@
<?php
namespace Gaufrette\Adapter;
use ZipArchive;
use Gaufrette\Adapter;
use Gaufrette\Util;
/**
* ZIP Archive adapter.
*
* @author Boris Guéry <guery.b@gmail.com>
* @author Antoine Hérault <antoine.herault@gmail.com>
*/
class Zip implements Adapter
{
/**
* @var string The zip archive full path
*/
protected $zipFile;
/**
* @var ZipArchive
*/
protected $zipArchive;
public function __construct($zipFile)
{
if (!extension_loaded('zip')) {
throw new \RuntimeException(sprintf(
'Unable to use %s as the ZIP extension is not available.',
__CLASS__
));
}
$this->zipFile = $zipFile;
$this->reinitZipArchive();
}
/**
* {@inheritdoc}
*/
public function read($key)
{
if (false === ($content = $this->zipArchive->getFromName($key, 0))) {
return false;
}
return $content;
}
/**
* {@inheritdoc}
*/
public function write($key, $content)
{
if (!$this->zipArchive->addFromString($key, $content)) {
return false;
}
if (!$this->save()) {
return false;
}
return Util\Size::fromContent($content);
}
/**
* {@inheritdoc}
*/
public function exists($key)
{
return (boolean) $this->getStat($key);
}
/**
* {@inheritdoc}
*/
public function keys()
{
$keys = array();
for ($i = 0; $i < $this->zipArchive->numFiles; ++$i) {
$keys[$i] = $this->zipArchive->getNameIndex($i);
}
return $keys;
}
/**
* @todo implement
*
* {@inheritdoc}
*/
public function isDirectory($key)
{
return false;
}
/**
* {@inheritdoc}
*/
public function mtime($key)
{
$stat = $this->getStat($key);
return isset($stat['mtime']) ? $stat['mtime'] : false;
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
if (!$this->zipArchive->deleteName($key)) {
return false;
}
return $this->save();
}
/**
* {@inheritdoc}
*/
public function rename($sourceKey, $targetKey)
{
if (!$this->zipArchive->renameName($sourceKey, $targetKey)) {
return false;
}
return $this->save();
}
/**
* Returns the stat of a file in the zip archive
* (name, index, crc, mtime, compression size, compression method, filesize).
*
* @param $key
*
* @return array|bool
*/
public function getStat($key)
{
$stat = $this->zipArchive->statName($key);
if (false === $stat) {
return array();
}
return $stat;
}
public function __destruct()
{
if ($this->zipArchive) {
try {
$this->zipArchive->close();
} catch (\Exception $e) {
}
unset($this->zipArchive);
}
}
protected function reinitZipArchive()
{
$this->zipArchive = new ZipArchive();
if (true !== ($resultCode = $this->zipArchive->open($this->zipFile, ZipArchive::CREATE))) {
switch ($resultCode) {
case ZipArchive::ER_EXISTS:
$errMsg = 'File already exists.';
break;
case ZipArchive::ER_INCONS:
$errMsg = 'Zip archive inconsistent.';
break;
case ZipArchive::ER_INVAL:
$errMsg = 'Invalid argument.';
break;
case ZipArchive::ER_MEMORY:
$errMsg = 'Malloc failure.';
break;
case ZipArchive::ER_NOENT:
$errMsg = 'Invalid argument.';
break;
case ZipArchive::ER_NOZIP:
$errMsg = 'Not a zip archive.';
break;
case ZipArchive::ER_OPEN:
$errMsg = 'Can\'t open file.';
break;
case ZipArchive::ER_READ:
$errMsg = 'Read error.';
break;
case ZipArchive::ER_SEEK;
$errMsg = 'Seek error.';
break;
default:
$errMsg = 'Unknown error.';
break;
}
throw new \RuntimeException(sprintf('%s', $errMsg));
}
return $this;
}
/**
* Saves archive modifications and updates current ZipArchive instance.
*
* @throws \RuntimeException If file could not be saved
*/
protected function save()
{
// Close to save modification
if (!$this->zipArchive->close()) {
return false;
}
// Re-initialize to get updated version
$this->reinitZipArchive();
return true;
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Gaufrette;
/**
* Interface for the Gaufrette related exceptions.
*
* @author Antoine Hérault <antoine.herault@gmail.com>
*/
interface Exception
{
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Gaufrette\Exception;
use Gaufrette\Exception;
/**
* Exception to be thrown when a file already exists.
*
* @author Benjamin Dulau <benjamin.dulau@gmail.com>
*/
class FileAlreadyExists extends \RuntimeException implements Exception
{
private $key;
public function __construct($key, $code = 0, \Exception $previous = null)
{
$this->key = $key;
parent::__construct(
sprintf('The file %s already exists and can not be overwritten.', $key),
$code,
$previous
);
}
public function getKey()
{
return $this->key;
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Gaufrette\Exception;
use Gaufrette\Exception;
/**
* Exception to be thrown when a file was not found.
*
* @author Antoine Hérault <antoine.herault@gmail.com>
*/
class FileNotFound extends \RuntimeException implements Exception
{
private $key;
public function __construct($key, $code = 0, \Exception $previous = null)
{
$this->key = $key;
parent::__construct(
sprintf('The file "%s" was not found.', $key),
$code,
$previous
);
}
public function getKey()
{
return $this->key;
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Gaufrette\Exception;
use Gaufrette\Exception;
/**
* Exception to be thrown when an unexpected file exists.
*
* @author Antoine Hérault <antoine.herault@gmail.com>
*/
class UnexpectedFile extends \RuntimeException implements Exception
{
private $key;
public function __construct($key, $code = 0, \Exception $previous = null)
{
$this->key = $key;
parent::__construct(
sprintf('The file "%s" was not supposed to exist.', $key),
$code,
$previous
);
}
public function getKey()
{
return $this->key;
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Gaufrette\Exception;
use Gaufrette\Exception;
class UnsupportedAdapterMethodException extends \BadMethodCallException implements Exception
{
}

View File

@@ -0,0 +1,233 @@
<?php
namespace Gaufrette;
use Gaufrette\Adapter\MetadataSupporter;
use Gaufrette\Exception\FileNotFound;
/**
* Points to a file in a filesystem.
*
* @author Antoine Hérault <antoine.herault@gmail.com>
*/
class File
{
protected $key;
protected $filesystem;
/**
* Content variable is lazy. It will not be read from filesystem until it's requested first time.
*
* @var mixed content
*/
protected $content = null;
/**
* @var array metadata in associative array. Only for adapters that support metadata
*/
protected $metadata = null;
/**
* Human readable filename (usually the end of the key).
*
* @var string name
*/
protected $name = null;
/**
* File size in bytes.
*
* @var int size
*/
protected $size = 0;
/**
* File date modified.
*
* @var int mtime
*/
protected $mtime = null;
/**
* @param string $key
* @param Filesystem $filesystem
*/
public function __construct($key, Filesystem $filesystem)
{
$this->key = $key;
$this->name = $key;
$this->filesystem = $filesystem;
}
/**
* Returns the key.
*
* @return string
*/
public function getKey()
{
return $this->key;
}
/**
* Returns the content.
*
* @throws FileNotFound
*
* @param array $metadata optional metadata which should be set when read
*
* @return string
*/
public function getContent($metadata = array())
{
if (isset($this->content)) {
return $this->content;
}
$this->setMetadata($metadata);
return $this->content = $this->filesystem->read($this->key);
}
/**
* @return string name of the file
*/
public function getName()
{
return $this->name;
}
/**
* @return int size of the file
*/
public function getSize()
{
if ($this->size) {
return $this->size;
}
try {
return $this->size = $this->filesystem->size($this->getKey());
} catch (FileNotFound $exception) {
}
return 0;
}
/**
* Returns the file modified time.
*
* @return int
*/
public function getMtime()
{
return $this->mtime = $this->filesystem->mtime($this->key);
}
/**
* @param int $size size of the file
*/
public function setSize($size)
{
$this->size = $size;
}
/**
* Sets the content.
*
* @param string $content
* @param array $metadata optional metadata which should be send when write
*
* @return int The number of bytes that were written into the file, or
* FALSE on failure
*/
public function setContent($content, $metadata = array())
{
$this->content = $content;
$this->setMetadata($metadata);
return $this->size = $this->filesystem->write($this->key, $this->content, true);
}
/**
* @param string $name name of the file
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Indicates whether the file exists in the filesystem.
*
* @return bool
*/
public function exists()
{
return $this->filesystem->has($this->key);
}
/**
* Deletes the file from the filesystem.
*
* @throws FileNotFound
* @throws \RuntimeException when cannot delete file
*
* @param array $metadata optional metadata which should be send when write
*
* @return bool TRUE on success
*/
public function delete($metadata = array())
{
$this->setMetadata($metadata);
return $this->filesystem->delete($this->key);
}
/**
* Creates a new file stream instance of the file.
*
* @return Stream
*/
public function createStream()
{
return $this->filesystem->createStream($this->key);
}
/**
* Rename the file and move it to its new location.
*
* @param string $newKey
*/
public function rename($newKey)
{
$this->filesystem->rename($this->key, $newKey);
$this->key = $newKey;
}
/**
* Sets the metadata array to be stored in adapters that can support it.
*
* @param array $metadata
*
* @return bool
*/
protected function setMetadata(array $metadata)
{
if ($metadata && $this->supportsMetadata()) {
$this->filesystem->getAdapter()->setMetadata($this->key, $metadata);
return true;
}
return false;
}
/**
* @return bool
*/
private function supportsMetadata()
{
return $this->filesystem->getAdapter() instanceof MetadataSupporter;
}
}

View File

@@ -0,0 +1,347 @@
<?php
namespace Gaufrette;
use Gaufrette\Adapter\ListKeysAware;
/**
* A filesystem is used to store and retrieve files.
*
* @author Antoine Hérault <antoine.herault@gmail.com>
* @author Leszek Prabucki <leszek.prabucki@gmail.com>
*/
class Filesystem implements FilesystemInterface
{
protected $adapter;
/**
* Contains File objects created with $this->createFile() method.
*
* @var array
*/
protected $fileRegister = array();
/**
* @param Adapter $adapter A configured Adapter instance
*/
public function __construct(Adapter $adapter)
{
$this->adapter = $adapter;
}
/**
* Returns the adapter.
*
* @return Adapter
*/
public function getAdapter()
{
return $this->adapter;
}
/**
* {@inheritdoc}
*/
public function has($key)
{
self::assertValidKey($key);
return $this->adapter->exists($key);
}
/**
* {@inheritdoc}
*/
public function rename($sourceKey, $targetKey)
{
self::assertValidKey($sourceKey);
self::assertValidKey($targetKey);
$this->assertHasFile($sourceKey);
if ($this->has($targetKey)) {
throw new Exception\UnexpectedFile($targetKey);
}
if (!$this->adapter->rename($sourceKey, $targetKey)) {
throw new \RuntimeException(sprintf('Could not rename the "%s" key to "%s".', $sourceKey, $targetKey));
}
if ($this->isFileInRegister($sourceKey)) {
$this->fileRegister[$targetKey] = $this->fileRegister[$sourceKey];
unset($this->fileRegister[$sourceKey]);
}
return true;
}
/**
* {@inheritdoc}
*/
public function get($key, $create = false)
{
self::assertValidKey($key);
if (!$create) {
$this->assertHasFile($key);
}
return $this->createFile($key);
}
/**
* {@inheritdoc}
*/
public function write($key, $content, $overwrite = false)
{
self::assertValidKey($key);
if (!$overwrite && $this->has($key)) {
throw new Exception\FileAlreadyExists($key);
}
$numBytes = $this->adapter->write($key, $content);
if (false === $numBytes) {
throw new \RuntimeException(sprintf('Could not write the "%s" key content.', $key));
}
return $numBytes;
}
/**
* {@inheritdoc}
*/
public function read($key)
{
self::assertValidKey($key);
$this->assertHasFile($key);
$content = $this->adapter->read($key);
if (false === $content) {
throw new \RuntimeException(sprintf('Could not read the "%s" key content.', $key));
}
return $content;
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
self::assertValidKey($key);
$this->assertHasFile($key);
if ($this->adapter->delete($key)) {
$this->removeFromRegister($key);
return true;
}
throw new \RuntimeException(sprintf('Could not remove the "%s" key.', $key));
}
/**
* {@inheritdoc}
*/
public function keys()
{
return $this->adapter->keys();
}
/**
* {@inheritdoc}
*/
public function listKeys($prefix = '')
{
if ($this->adapter instanceof ListKeysAware) {
return $this->adapter->listKeys($prefix);
}
$dirs = array();
$keys = array();
foreach ($this->keys() as $key) {
if (empty($prefix) || 0 === strpos($key, $prefix)) {
if ($this->adapter->isDirectory($key)) {
$dirs[] = $key;
} else {
$keys[] = $key;
}
}
}
return array(
'keys' => $keys,
'dirs' => $dirs,
);
}
/**
* {@inheritdoc}
*/
public function mtime($key)
{
self::assertValidKey($key);
$this->assertHasFile($key);
return $this->adapter->mtime($key);
}
/**
* {@inheritdoc}
*/
public function checksum($key)
{
self::assertValidKey($key);
$this->assertHasFile($key);
if ($this->adapter instanceof Adapter\ChecksumCalculator) {
return $this->adapter->checksum($key);
}
return Util\Checksum::fromContent($this->read($key));
}
/**
* {@inheritdoc}
*/
public function size($key)
{
self::assertValidKey($key);
$this->assertHasFile($key);
if ($this->adapter instanceof Adapter\SizeCalculator) {
return $this->adapter->size($key);
}
return Util\Size::fromContent($this->read($key));
}
/**
* {@inheritdoc}
*/
public function createStream($key)
{
self::assertValidKey($key);
if ($this->adapter instanceof Adapter\StreamFactory) {
return $this->adapter->createStream($key);
}
return new Stream\InMemoryBuffer($this, $key);
}
/**
* {@inheritdoc}
*/
public function createFile($key)
{
self::assertValidKey($key);
if (false === $this->isFileInRegister($key)) {
if ($this->adapter instanceof Adapter\FileFactory) {
$this->fileRegister[$key] = $this->adapter->createFile($key, $this);
} else {
$this->fileRegister[$key] = new File($key, $this);
}
}
return $this->fileRegister[$key];
}
/**
* {@inheritdoc}
*/
public function mimeType($key)
{
self::assertValidKey($key);
$this->assertHasFile($key);
if ($this->adapter instanceof Adapter\MimeTypeProvider) {
return $this->adapter->mimeType($key);
}
throw new \LogicException(sprintf(
'Adapter "%s" cannot provide MIME type',
get_class($this->adapter)
));
}
/**
* Checks if matching file by given key exists in the filesystem.
*
* Key must be non empty string, otherwise it will throw Exception\FileNotFound
* {@see http://php.net/manual/en/function.empty.php}
*
* @param string $key
*
* @throws Exception\FileNotFound when sourceKey does not exist
*/
private function assertHasFile($key)
{
if (!$this->has($key)) {
throw new Exception\FileNotFound($key);
}
}
/**
* Checks if matching File object by given key exists in the fileRegister.
*
* @param string $key
*
* @return bool
*/
private function isFileInRegister($key)
{
return array_key_exists($key, $this->fileRegister);
}
/**
* Clear files register.
*/
public function clearFileRegister()
{
$this->fileRegister = array();
}
/**
* Removes File object from register.
*
* @param string $key
*/
public function removeFromRegister($key)
{
if ($this->isFileInRegister($key)) {
unset($this->fileRegister[$key]);
}
}
/**
* {@inheritdoc}
*/
public function isDirectory($key)
{
return $this->adapter->isDirectory($key);
}
/**
* @param string $key
*
* @throws \InvalidArgumentException Given $key should not be empty
*/
private static function assertValidKey($key)
{
if (empty($key)) {
throw new \InvalidArgumentException('Object path is empty.');
}
}
}

View File

@@ -0,0 +1,183 @@
<?php
namespace Gaufrette;
interface FilesystemInterface
{
/**
* Indicates whether the file matching the specified key exists.
*
* @param string $key
*
* @return bool TRUE if the file exists, FALSE otherwise
*
* @throws \InvalidArgumentException If $key is invalid
*/
public function has($key);
/**
* Renames a file.
*
* File::rename should be preferred or you may face bad filesystem consistency.
*
* @param string $sourceKey
* @param string $targetKey
*
* @return bool TRUE if the rename was successful
*
* @throws Exception\FileNotFound when sourceKey does not exist
* @throws Exception\UnexpectedFile when targetKey exists
* @throws \RuntimeException when cannot rename
* @throws \InvalidArgumentException If $sourceKey or $targetKey are invalid
*
* @see File::rename()
*/
public function rename($sourceKey, $targetKey);
/**
* Returns the file matching the specified key.
*
* @param string $key Key of the file
* @param bool $create Whether to create the file if it does not exist
*
* @throws Exception\FileNotFound
* @throws \InvalidArgumentException If $key is invalid
*
* @return File
*/
public function get($key, $create = false);
/**
* Writes the given content into the file.
*
* @param string $key Key of the file
* @param string $content Content to write in the file
* @param bool $overwrite Whether to overwrite the file if exists
*
* @throws Exception\FileAlreadyExists When file already exists and overwrite is false
* @throws \RuntimeException When for any reason content could not be written
* @throws \InvalidArgumentException If $key is invalid
*
* @return int The number of bytes that were written into the file
*/
public function write($key, $content, $overwrite = false);
/**
* Reads the content from the file.
*
* @param string $key Key of the file
*
* @throws Exception\FileNotFound when file does not exist
* @throws \RuntimeException when cannot read file
* @throws \InvalidArgumentException If $key is invalid
*
* @return string
*/
public function read($key);
/**
* Deletes the file matching the specified key.
*
* @param string $key
*
* @throws \RuntimeException when cannot read file
* @throws \InvalidArgumentException If $key is invalid
*
* @return bool
*/
public function delete($key);
/**
* Returns an array of all keys.
*
* @return array
*/
public function keys();
/**
* Lists keys beginning with given prefix
* (no wildcard / regex matching).
*
* if adapter implements ListKeysAware interface, adapter's implementation will be used,
* in not, ALL keys will be requested and iterated through.
*
* @param string $prefix
*
* @return array
*/
public function listKeys($prefix = '');
/**
* Returns the last modified time of the specified file.
*
* @param string $key
*
* @return int An UNIX like timestamp
*
* @throws \InvalidArgumentException If $key is invalid
*/
public function mtime($key);
/**
* Returns the checksum of the specified file's content.
*
* @param string $key
*
* @return string A MD5 hash
*
* @throws \InvalidArgumentException If $key is invalid
*/
public function checksum($key);
/**
* Returns the size of the specified file's content.
*
* @param string $key
*
* @return int File size in Bytes
*
* @throws \InvalidArgumentException If $key is invalid
*/
public function size($key);
/**
* Gets a new stream instance of the specified file.
*
* @param $key
*
* @return Stream|Stream\InMemoryBuffer
*
* @throws \InvalidArgumentException If $key is invalid
*/
public function createStream($key);
/**
* Creates a new file in a filesystem.
*
* @param $key
*
* @return File
*
* @throws \InvalidArgumentException If $key is invalid
*/
public function createFile($key);
/**
* Get the mime type of the provided key.
*
* @param string $key
*
* @return string
*
* @throws \InvalidArgumentException If $key is invalid
*/
public function mimeType($key);
/**
* @param string $key
*
* @return bool
*/
public function isDirectory($key);
}

View File

@@ -0,0 +1,105 @@
<?php
namespace Gaufrette;
/**
* Associates filesystem instances to domains.
*
* @author Antoine Hérault <antoine.herault@gmail.com>
*/
class FilesystemMap
{
private $filesystems = array();
/**
* Returns an array of all the registered filesystems where the key is the
* domain and the value the filesystem.
*
* @return array
*/
public function all()
{
return $this->filesystems;
}
/**
* Register the given filesystem for the specified domain.
*
* @param string $domain
* @param FilesystemInterface $filesystem
*
* @throws \InvalidArgumentException when the specified domain contains
* forbidden characters
*/
public function set($domain, FilesystemInterface $filesystem)
{
if (!preg_match('/^[-_a-zA-Z0-9]+$/', $domain)) {
throw new \InvalidArgumentException(sprintf(
'The specified domain "%s" is not a valid domain.',
$domain
));
}
$this->filesystems[$domain] = $filesystem;
}
/**
* Indicates whether there is a filesystem registered for the specified
* domain.
*
* @param string $domain
*
* @return bool
*/
public function has($domain)
{
return isset($this->filesystems[$domain]);
}
/**
* Returns the filesystem registered for the specified domain.
*
* @param string $domain
*
* @return FilesystemInterface
*
* @throw \InvalidArgumentException when there is no filesystem registered
* for the specified domain
*/
public function get($domain)
{
if (!$this->has($domain)) {
throw new \InvalidArgumentException(sprintf(
'There is no filesystem defined for the "%s" domain.',
$domain
));
}
return $this->filesystems[$domain];
}
/**
* Removes the filesystem registered for the specified domain.
*
* @param string $domain
*/
public function remove($domain)
{
if (!$this->has($domain)) {
throw new \InvalidArgumentException(sprintf(
'Cannot remove the "%s" filesystem as it is not defined.',
$domain
));
}
unset($this->filesystems[$domain]);
}
/**
* Clears all the registered filesystems.
*/
public function clear()
{
$this->filesystems = array();
}
}

View File

@@ -0,0 +1,109 @@
<?php
namespace Gaufrette;
/**
* Interface for the file streams.
*
* @author Antoine Hérault <antoine.herault@gmail.com>
*/
interface Stream
{
/**
* Opens the stream in the specified mode.
*
* @param StreamMode $mode
*
* @return bool TRUE on success or FALSE on failure
*/
public function open(StreamMode $mode);
/**
* Reads the specified number of bytes from the current position.
*
* If the current position is the end-of-file, you must return an empty
* string.
*
* @param int $count The number of bytes
*
* @return string
*/
public function read($count);
/**
* Writes the specified data.
*
* Don't forget to update the current position of the stream by number of
* bytes that were successfully written.
*
* @param string $data
*
* @return int The number of bytes that were successfully written
*/
public function write($data);
/**
* Closes the stream.
*
* It must free all the resources. If there is any data to flush, you
* should do so
*/
public function close();
/**
* Flushes the output.
*
* If you have cached data that is not yet stored into the underlying
* storage, you should do so now
*
* @return bool TRUE on success or FALSE on failure
*/
public function flush();
/**
* Seeks to the specified offset.
*
* @param int $offset
* @param int $whence
*
* @return bool
*/
public function seek($offset, $whence = SEEK_SET);
/**
* Returns the current position.
*
* @return int
*/
public function tell();
/**
* Indicates whether the current position is the end-of-file.
*
* @return bool
*/
public function eof();
/**
* Gathers statistics of the stream.
*
* @return array
*/
public function stat();
/**
* Retrieve the underlying resource.
*
* @param int $castAs
*
* @return mixed using resource or false
*/
public function cast($castAs);
/**
* Delete a file.
*
* @return bool TRUE on success FALSE otherwise
*/
public function unlink();
}

View File

@@ -0,0 +1,224 @@
<?php
namespace Gaufrette\Stream;
use Gaufrette\Stream;
use Gaufrette\Filesystem;
use Gaufrette\StreamMode;
use Gaufrette\Util;
class InMemoryBuffer implements Stream
{
private $filesystem;
private $key;
private $mode;
private $content;
private $numBytes;
private $position;
private $synchronized;
/**
* @param Filesystem $filesystem The filesystem managing the file to stream
* @param string $key The file key
*/
public function __construct(Filesystem $filesystem, $key)
{
$this->filesystem = $filesystem;
$this->key = $key;
}
/**
* {@inheritdoc}
*/
public function open(StreamMode $mode)
{
$this->mode = $mode;
$exists = $this->filesystem->has($this->key);
if (($exists && !$mode->allowsExistingFileOpening())
|| (!$exists && !$mode->allowsNewFileOpening())) {
return false;
}
if ($mode->impliesExistingContentDeletion()) {
$this->content = $this->writeContent('');
} elseif (!$exists && $mode->allowsNewFileOpening()) {
$this->content = $this->writeContent('');
} else {
$this->content = $this->filesystem->read($this->key);
}
$this->numBytes = Util\Size::fromContent($this->content);
$this->position = $mode->impliesPositioningCursorAtTheEnd() ? $this->numBytes : 0;
$this->synchronized = true;
return true;
}
public function read($count)
{
if (false === $this->mode->allowsRead()) {
throw new \LogicException('The stream does not allow read.');
}
$chunk = substr($this->content, $this->position, $count);
$this->position += Util\Size::fromContent($chunk);
return $chunk;
}
public function write($data)
{
if (false === $this->mode->allowsWrite()) {
throw new \LogicException('The stream does not allow write.');
}
$numWrittenBytes = Util\Size::fromContent($data);
$newPosition = $this->position + $numWrittenBytes;
$newNumBytes = $newPosition > $this->numBytes ? $newPosition : $this->numBytes;
if ($this->eof()) {
$this->numBytes += $numWrittenBytes;
if ($this->hasNewContentAtFurtherPosition()) {
$data = str_pad($data, $this->position + strlen($data), ' ', STR_PAD_LEFT);
}
$this->content .= $data;
} else {
$before = substr($this->content, 0, $this->position);
$after = $newNumBytes > $newPosition ? substr($this->content, $newPosition) : '';
$this->content = $before.$data.$after;
}
$this->position = $newPosition;
$this->numBytes = $newNumBytes;
$this->synchronized = false;
return $numWrittenBytes;
}
public function close()
{
if (!$this->synchronized) {
$this->flush();
}
}
public function seek($offset, $whence = SEEK_SET)
{
switch ($whence) {
case SEEK_SET:
$this->position = $offset;
break;
case SEEK_CUR:
$this->position += $offset;
break;
case SEEK_END:
$this->position = $this->numBytes + $offset;
break;
default:
return false;
}
return true;
}
public function tell()
{
return $this->position;
}
public function flush()
{
if ($this->synchronized) {
return true;
}
try {
$this->writeContent($this->content);
} catch (\Exception $e) {
return false;
}
return true;
}
public function eof()
{
return $this->position >= $this->numBytes;
}
/**
* {@inheritdoc}
*/
public function stat()
{
if ($this->filesystem->has($this->key)) {
$isDirectory = $this->filesystem->isDirectory($this->key);
$time = $this->filesystem->mtime($this->key);
$stats = array(
'dev' => 1,
'ino' => 0,
'mode' => $isDirectory ? 16893 : 33204,
'nlink' => 1,
'uid' => 0,
'gid' => 0,
'rdev' => 0,
'size' => $isDirectory ? 0 : Util\Size::fromContent($this->content),
'atime' => $time,
'mtime' => $time,
'ctime' => $time,
'blksize' => -1,
'blocks' => -1,
);
return array_merge(array_values($stats), $stats);
}
return false;
}
/**
* {@inheritdoc}
*/
public function cast($castAst)
{
return false;
}
/**
* {@inheritdoc}
*/
public function unlink()
{
if ($this->mode && $this->mode->impliesExistingContentDeletion()) {
return $this->filesystem->delete($this->key);
}
return false;
}
/**
* @return bool
*/
protected function hasNewContentAtFurtherPosition()
{
return $this->position > 0 && !$this->content;
}
/**
* @param string $content Empty string by default
* @param bool $overwrite Overwrite by default
*
* @return string
*/
protected function writeContent($content = '', $overwrite = true)
{
$this->filesystem->write($this->key, $content, $overwrite);
return $content;
}
}

View File

@@ -0,0 +1,191 @@
<?php
namespace Gaufrette\Stream;
use Gaufrette\Stream;
use Gaufrette\StreamMode;
/**
* Local stream.
*
* @author Antoine Hérault <antoine.herault@gmail.com>
*/
class Local implements Stream
{
private $path;
private $mode;
private $fileHandle;
private $mkdirMode;
/**
* @param string $path
* @param int $mkdirMode
*/
public function __construct($path, $mkdirMode = 0755)
{
$this->path = $path;
$this->mkdirMode = $mkdirMode;
}
/**
* {@inheritdoc}
*/
public function open(StreamMode $mode)
{
$baseDirPath = \Gaufrette\Util\Path::dirname($this->path);
if ($mode->allowsWrite() && !is_dir($baseDirPath)) {
@mkdir($baseDirPath, $this->mkdirMode, true);
}
try {
$fileHandle = @fopen($this->path, $mode->getMode());
} catch (\Exception $e) {
$fileHandle = false;
}
if (false === $fileHandle) {
throw new \RuntimeException(sprintf('File "%s" cannot be opened', $this->path));
}
$this->mode = $mode;
$this->fileHandle = $fileHandle;
return true;
}
/**
* {@inheritdoc}
*/
public function read($count)
{
if (!$this->fileHandle) {
return false;
}
if (false === $this->mode->allowsRead()) {
throw new \LogicException('The stream does not allow read.');
}
return fread($this->fileHandle, $count);
}
/**
* {@inheritdoc}
*/
public function write($data)
{
if (!$this->fileHandle) {
return false;
}
if (false === $this->mode->allowsWrite()) {
throw new \LogicException('The stream does not allow write.');
}
return fwrite($this->fileHandle, $data);
}
/**
* {@inheritdoc}
*/
public function close()
{
if (!$this->fileHandle) {
return false;
}
$closed = fclose($this->fileHandle);
if ($closed) {
$this->mode = null;
$this->fileHandle = null;
}
return $closed;
}
/**
* {@inheritdoc}
*/
public function flush()
{
if ($this->fileHandle) {
return fflush($this->fileHandle);
}
return false;
}
/**
* {@inheritdoc}
*/
public function seek($offset, $whence = SEEK_SET)
{
if ($this->fileHandle) {
return 0 === fseek($this->fileHandle, $offset, $whence);
}
return false;
}
/**
* {@inheritdoc}
*/
public function tell()
{
if ($this->fileHandle) {
return ftell($this->fileHandle);
}
return false;
}
/**
* {@inheritdoc}
*/
public function eof()
{
if ($this->fileHandle) {
return feof($this->fileHandle);
}
return true;
}
/**
* {@inheritdoc}
*/
public function stat()
{
if ($this->fileHandle) {
return fstat($this->fileHandle);
} elseif (!is_resource($this->fileHandle) && is_dir($this->path)) {
return stat($this->path);
}
return false;
}
/**
* {@inheritdoc}
*/
public function cast($castAs)
{
if ($this->fileHandle) {
return $this->fileHandle;
}
return false;
}
/**
* {@inheritdoc}
*/
public function unlink()
{
if ($this->mode && $this->mode->impliesExistingContentDeletion()) {
return @unlink($this->path);
}
return false;
}
}

View File

@@ -0,0 +1,142 @@
<?php
namespace Gaufrette;
/**
* Represents a stream mode.
*
* @author Antoine Hérault <antoine.herault@gmail.com>
*/
class StreamMode
{
private $mode;
private $base;
private $plus;
private $flag;
/**
* @param string $mode A stream mode as for the use of fopen()
*/
public function __construct($mode)
{
$this->mode = $mode;
$mode = substr($mode, 0, 3);
$rest = substr($mode, 1);
$this->base = substr($mode, 0, 1);
$this->plus = false !== strpos($rest, '+');
$this->flag = trim($rest, '+');
}
/**
* Returns the underlying mode.
*
* @return string
*/
public function getMode()
{
return $this->mode;
}
/**
* Indicates whether the mode allows to read.
*
* @return bool
*/
public function allowsRead()
{
if ($this->plus) {
return true;
}
return 'r' === $this->base;
}
/**
* Indicates whether the mode allows to write.
*
* @return bool
*/
public function allowsWrite()
{
if ($this->plus) {
return true;
}
return 'r' !== $this->base;
}
/**
* Indicates whether the mode allows to open an existing file.
*
* @return bool
*/
public function allowsExistingFileOpening()
{
return 'x' !== $this->base;
}
/**
* Indicates whether the mode allows to create a new file.
*
* @return bool
*/
public function allowsNewFileOpening()
{
return 'r' !== $this->base;
}
/**
* Indicates whether the mode implies to delete the existing content of the
* file when it already exists.
*
* @return bool
*/
public function impliesExistingContentDeletion()
{
return 'w' === $this->base;
}
/**
* Indicates whether the mode implies positioning the cursor at the
* beginning of the file.
*
* @return bool
*/
public function impliesPositioningCursorAtTheBeginning()
{
return 'a' !== $this->base;
}
/**
* Indicates whether the mode implies positioning the cursor at the end of
* the file.
*
* @return bool
*/
public function impliesPositioningCursorAtTheEnd()
{
return 'a' === $this->base;
}
/**
* Indicates whether the stream is in binary mode.
*
* @return bool
*/
public function isBinary()
{
return 'b' === $this->flag;
}
/**
* Indicates whether the stream is in text mode.
*
* @return bool
*/
public function isText()
{
return false === $this->isBinary();
}
}

View File

@@ -0,0 +1,281 @@
<?php
namespace Gaufrette;
/**
* Stream wrapper class for the Gaufrette filesystems.
*
* @author Antoine Hérault <antoine.herault@gmail.com>
* @author Leszek Prabucki <leszek.prabucki@gmail.com>
*/
class StreamWrapper
{
private static $filesystemMap;
private $stream;
/**
* Defines the filesystem map.
*
* @param FilesystemMap $map
*/
public static function setFilesystemMap(FilesystemMap $map)
{
static::$filesystemMap = $map;
}
/**
* Returns the filesystem map.
*
* @return FilesystemMap $map
*/
public static function getFilesystemMap()
{
if (null === static::$filesystemMap) {
static::$filesystemMap = static::createFilesystemMap();
}
return static::$filesystemMap;
}
/**
* Registers the stream wrapper to handle the specified scheme.
*
* @param string $scheme Default is gaufrette
*/
public static function register($scheme = 'gaufrette')
{
static::streamWrapperUnregister($scheme);
if (!static::streamWrapperRegister($scheme, __CLASS__)) {
throw new \RuntimeException(sprintf(
'Could not register stream wrapper class %s for scheme %s.',
__CLASS__,
$scheme
));
}
}
/**
* @return FilesystemMap
*/
protected static function createFilesystemMap()
{
return new FilesystemMap();
}
/**
* @param string $scheme - protocol scheme
*/
protected static function streamWrapperUnregister($scheme)
{
if (in_array($scheme, stream_get_wrappers())) {
return stream_wrapper_unregister($scheme);
}
}
/**
* @param string $scheme - protocol scheme
* @param string $className
*
* @return bool
*/
protected static function streamWrapperRegister($scheme, $className)
{
return stream_wrapper_register($scheme, $className);
}
public function stream_open($path, $mode)
{
$this->stream = $this->createStream($path);
return $this->stream->open($this->createStreamMode($mode));
}
/**
* @param int $bytes
*
* @return mixed
*/
public function stream_read($bytes)
{
if ($this->stream) {
return $this->stream->read($bytes);
}
return false;
}
/**
* @param string $data
*
* @return int
*/
public function stream_write($data)
{
if ($this->stream) {
return $this->stream->write($data);
}
return 0;
}
public function stream_close()
{
if ($this->stream) {
$this->stream->close();
}
}
/**
* @return bool
*/
public function stream_flush()
{
if ($this->stream) {
return $this->stream->flush();
}
return false;
}
/**
* @param int $offset
* @param int $whence - one of values [SEEK_SET, SEEK_CUR, SEEK_END]
*
* @return bool
*/
public function stream_seek($offset, $whence = SEEK_SET)
{
if ($this->stream) {
return $this->stream->seek($offset, $whence);
}
return false;
}
/**
* @return mixed
*/
public function stream_tell()
{
if ($this->stream) {
return $this->stream->tell();
}
return false;
}
/**
* @return bool
*/
public function stream_eof()
{
if ($this->stream) {
return $this->stream->eof();
}
return true;
}
/**
* @return mixed
*/
public function stream_stat()
{
if ($this->stream) {
return $this->stream->stat();
}
return false;
}
/**
* @param string $path
* @param int $flags
*
* @return mixed
*
* @todo handle $flags parameter
*/
public function url_stat($path, $flags)
{
$stream = $this->createStream($path);
try {
$stream->open($this->createStreamMode('r+'));
} catch (\RuntimeException $e) {
}
return $stream->stat();
}
/**
* @param string $path
*
* @return mixed
*/
public function unlink($path)
{
$stream = $this->createStream($path);
try {
$stream->open($this->createStreamMode('w+'));
} catch (\RuntimeException $e) {
return false;
}
return $stream->unlink();
}
/**
* @return mixed
*/
public function stream_cast($castAs)
{
if ($this->stream) {
return $this->stream->cast($castAs);
}
return false;
}
protected function createStream($path)
{
$parts = array_merge(
array(
'scheme' => null,
'host' => null,
'path' => null,
'query' => null,
'fragment' => null,
),
parse_url($path) ?: array()
);
$domain = $parts['host'];
$key = substr($parts['path'], 1);
if (null !== $parts['query']) {
$key .= '?'.$parts['query'];
}
if (null !== $parts['fragment']) {
$key .= '#'.$parts['fragment'];
}
if (empty($domain) || empty($key)) {
throw new \InvalidArgumentException(sprintf(
'The specified path (%s) is invalid.',
$path
));
}
return static::getFilesystemMap()->get($domain)->createStream($key);
}
protected function createStreamMode($mode)
{
return new StreamMode($mode);
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Gaufrette\Util;
/**
* Checksum utils.
*
* @author Antoine Hérault <antoine.herault@gmail.com>
*/
class Checksum
{
/**
* Returns the checksum of the given content.
*
* @param string $content
*
* @return string
*/
public static function fromContent($content)
{
return md5($content);
}
/**
* Returns the checksum of the specified file.
*
* @param string $filename
*
* @return string
*/
public static function fromFile($filename)
{
return md5_file($filename);
}
}

View File

@@ -0,0 +1,89 @@
<?php
namespace Gaufrette\Util;
/**
* Path utils.
*
* @author Antoine Hérault <antoine.herault@gmail.com>
*/
class Path
{
/**
* Normalizes the given path.
*
* @param string $path
*
* @return string
*/
public static function normalize($path)
{
$path = str_replace('\\', '/', $path);
$prefix = static::getAbsolutePrefix($path);
$path = substr($path, strlen($prefix));
$parts = array_filter(explode('/', $path), 'strlen');
$tokens = array();
foreach ($parts as $part) {
switch ($part) {
case '.':
continue;
case '..':
if (0 !== count($tokens)) {
array_pop($tokens);
continue;
} elseif (!empty($prefix)) {
continue;
}
default:
$tokens[] = $part;
}
}
return $prefix.implode('/', $tokens);
}
/**
* Indicates whether the given path is absolute or not.
*
* @param string $path A normalized path
*
* @return bool
*/
public static function isAbsolute($path)
{
return '' !== static::getAbsolutePrefix($path);
}
/**
* Returns the absolute prefix of the given path.
*
* @param string $path A normalized path
*
* @return string
*/
public static function getAbsolutePrefix($path)
{
preg_match('|^(?P<prefix>([a-zA-Z]+:)?//?)|', $path, $matches);
if (empty($matches['prefix'])) {
return '';
}
return strtolower($matches['prefix']);
}
/**
* Wrap native dirname function in order to handle only UNIX-style paths
*
* @param string $path
*
* @return string
*
* @see http://php.net/manual/en/function.dirname.php
*/
public static function dirname($path)
{
return str_replace('\\', '/', \dirname($path));
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace Gaufrette\Util;
/**
* Utility class for file sizes.
*
* @author Antoine Hérault <antoine.herault@gmail.com>
*/
class Size
{
/**
* Returns the size in bytes from the given content.
*
* @param string $content
*
* @return int
*
* @todo handle the case the mbstring is not loaded
*/
public static function fromContent($content)
{
// Make sure to get the real length in byte and not
// accidentally mistake some bytes as a UTF BOM.
return mb_strlen($content, '8bit');
}
/**
* Returns the size in bytes from the given file.
*
* @param string $filename
*
* @return int
*/
public static function fromFile($filename)
{
return filesize($filename);
}
/**
* Returns the size in bytes from the given resource.
*
* @param resource $handle
*
* @return string
*/
public static function fromResource($handle)
{
$cStat = fstat($handle);
// if the resource is a remote file, $cStat will be false
return $cStat ? $cStat['size'] : 0;
}
}