diff --git a/.travis.yml b/.travis.yml index 0bc8f6589..4bb90fd4e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,7 @@ before_install: - sh -c 'if [ "${TRAVIS_PHP_VERSION}" != "hhvm" ]; then echo "memory_limit = -1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; fi;' - sh -c 'if [ "${TRAVIS_PHP_VERSION}" != "hhvm" && "${TRAVIS_PHP_VERSION}" != "7.0" ]; then yes "" | pecl -q install -f mongo; fi;' - sh -c 'if [ "${TRAVIS_PHP_VERSION}" != "hhvm" && "${TRAVIS_PHP_VERSION}" != "7.0" ]; then composer require --dev doctrine/mongodb-odm:~1.0 --no-update; fi;' + - sh -c 'if [ "${TRAVIS_PHP_VERSION}" != "hhvm" && "${TRAVIS_PHP_VERSION}" >= "5.4" ]; then composer require --dev league/flysystem:~1.0 --no-update; fi;' - composer self-update - if [ "$SYMFONY_VERSION" != "" ]; then composer require "symfony/symfony:${SYMFONY_VERSION}" --no-update; fi; diff --git a/Binary/Loader/FlysystemLoader.php b/Binary/Loader/FlysystemLoader.php new file mode 100644 index 000000000..1d0902ab9 --- /dev/null +++ b/Binary/Loader/FlysystemLoader.php @@ -0,0 +1,47 @@ +extensionGuesser = $extensionGuesser; + $this->filesystem = $filesystem; + } + + /** + * {@inheritdoc} + */ + public function find($path) + { + if ($this->filesystem->has($path) === false) { + throw new NotLoadableException(sprintf('Source image "%s" not found.', $path)); + } + + $mimeType = $this->filesystem->getMimetype($path); + + return new Binary( + $this->filesystem->read($path), + $mimeType, + $this->extensionGuesser->guess($mimeType) + ); + } +} diff --git a/DependencyInjection/Factory/Loader/FlysystemLoaderFactory.php b/DependencyInjection/Factory/Loader/FlysystemLoaderFactory.php new file mode 100644 index 000000000..d5a417c5e --- /dev/null +++ b/DependencyInjection/Factory/Loader/FlysystemLoaderFactory.php @@ -0,0 +1,48 @@ +replaceArgument(1, new Reference($config['filesystem_service'])); + $loaderDefinition->addTag('liip_imagine.binary.loader', array( + 'loader' => $loaderName, + )); + $loaderId = 'liip_imagine.binary.loader.'.$loaderName; + + $container->setDefinition($loaderId, $loaderDefinition); + + return $loaderId; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'flysystem'; + } + + /** + * {@inheritdoc} + */ + public function addConfiguration(ArrayNodeDefinition $builder) + { + $builder + ->children() + ->scalarNode('filesystem_service')->isRequired()->cannotBeEmpty()->end() + ->end() + ; + } +} diff --git a/LiipImagineBundle.php b/LiipImagineBundle.php index 5894f33a7..0e080cdf1 100644 --- a/LiipImagineBundle.php +++ b/LiipImagineBundle.php @@ -8,6 +8,7 @@ use Liip\ImagineBundle\DependencyInjection\Compiler\ResolversCompilerPass; use Liip\ImagineBundle\DependencyInjection\Factory\Loader\FileSystemLoaderFactory; use Liip\ImagineBundle\DependencyInjection\Factory\Loader\StreamLoaderFactory; +use Liip\ImagineBundle\DependencyInjection\Factory\Loader\FlysystemLoaderFactory; use Liip\ImagineBundle\DependencyInjection\Factory\Resolver\AwsS3ResolverFactory; use Liip\ImagineBundle\DependencyInjection\Factory\Resolver\WebPathResolverFactory; use Liip\ImagineBundle\DependencyInjection\LiipImagineExtension; @@ -36,5 +37,6 @@ public function build(ContainerBuilder $container) $extension->addLoaderFactory(new StreamLoaderFactory()); $extension->addLoaderFactory(new FileSystemLoaderFactory()); + $extension->addLoaderFactory(new FlysystemLoaderFactory()); } } diff --git a/Resources/config/imagine.xml b/Resources/config/imagine.xml index dc4928539..ead54cc31 100644 --- a/Resources/config/imagine.xml +++ b/Resources/config/imagine.xml @@ -49,6 +49,7 @@ Liip\ImagineBundle\Binary\Loader\FileSystemLoader Liip\ImagineBundle\Binary\Loader\StreamLoader + Liip\ImagineBundle\Binary\Loader\FlysystemLoader @@ -205,6 +206,11 @@ + + + + + diff --git a/Resources/doc/configuration.rst b/Resources/doc/configuration.rst index 72a075581..289665322 100644 --- a/Resources/doc/configuration.rst +++ b/Resources/doc/configuration.rst @@ -56,10 +56,10 @@ There are several configuration options available: * ``data_loader`` - name of a custom data loader. Default value: ``filesystem`` (which means the standard filesystem loader is used). * ``controller`` - * ``filter_action`` - name of the controller action to use in the route loader. - Default value: ``liip_imagine.controller:filterAction`` - * ``filter_runtime_action`` - name of the controller action to use in the route - loader for runtimeconfig images. Default value: ``liip_imagine.controller:filterRuntimeAction`` + * ``filter_action`` - name of the controller action to use in the route loader. + Default value: ``liip_imagine.controller:filterAction`` + * ``filter_runtime_action`` - name of the controller action to use in the route + loader for runtimeconfig images. Default value: ``liip_imagine.controller:filterRuntimeAction`` * ``driver`` - one of the three drivers: ``gd``, ``imagick``, ``gmagick``. Default value: ``gd`` * ``filter_sets`` - specify the filter sets that you want to define and use. diff --git a/Resources/doc/data-loader/flysystem.rst b/Resources/doc/data-loader/flysystem.rst new file mode 100644 index 000000000..b41706d6d --- /dev/null +++ b/Resources/doc/data-loader/flysystem.rst @@ -0,0 +1,35 @@ +FlysystemLoader +=============== + +This loader lets you load images from `Flysystem`_ filesystem abstraction layer, +which can be used in Symfony projects by installing, for example, `OneupFlysystemBundle`_. + +Value of ``filesystem_service`` property must be a service, +which returns an instance of League\\Flysystem\\Filesystem. + +For implementation using `OneupFlysystemBundle`_ look below. + +Using factory +------------- + +.. code-block:: yaml + + liip_imagine: + loaders: + profile_photos: + flysystem: + filesystem_service: oneup_flysystem.profile_photos_filesystem + + oneup_flysystem: + adapters: + profile_photos: + local: + directory: "path/to/profile/photos" + + filesystems: + profile_photos: + adapter: profile_photos + + +.. _`Flysystem`: /~https://github.com/thephpleague/flysystem +.. _`OneupFlysystemBundle`: /~https://github.com/1up-lab/OneupFlysystemBundle diff --git a/Resources/doc/data-loaders.rst b/Resources/doc/data-loaders.rst index d42f759b4..f05de8a8b 100644 --- a/Resources/doc/data-loaders.rst +++ b/Resources/doc/data-loaders.rst @@ -7,6 +7,7 @@ Built-In DataLoader data-loader/filesystem data-loader/gridfs data-loader/stream + data-loader/flysystem Other data loaders ------------------ diff --git a/Resources/doc/filters.rst b/Resources/doc/filters.rst index 60a516614..7307d1119 100644 --- a/Resources/doc/filters.rst +++ b/Resources/doc/filters.rst @@ -237,7 +237,7 @@ To tell the bundle about your new filter loader, register it in the service container and apply the ``liip_imagine.filter.loader`` tag to it (example here in XML): -.. contiguration-block:: +.. configuration-block:: .. code-block:: yaml @@ -322,7 +322,7 @@ should return the ``BinaryInterface`` as well. To tell the bundle about your post-processor, register it in the service container and apply the ``liip_imagine.filter.post_processor`` tag to it: -.. contiguration-block:: +.. configuration-block:: .. code-block:: yaml diff --git a/Tests/Binary/Loader/FlysystemLoaderTest.php b/Tests/Binary/Loader/FlysystemLoaderTest.php new file mode 100644 index 000000000..afd2c1268 --- /dev/null +++ b/Tests/Binary/Loader/FlysystemLoaderTest.php @@ -0,0 +1,73 @@ +markTestSkipped( + 'The league/flysystem PHP library is not available.' + ); + } + + $adapter = new \League\Flysystem\Adapter\Local($this->fixturesDir); + $this->flyFilesystem = new \League\Flysystem\Filesystem($adapter); + } + + public function testShouldImplementLoaderInterface() + { + $rc = new \ReflectionClass('Liip\ImagineBundle\Binary\Loader\FlysystemLoader'); + + $this->assertTrue($rc->implementsInterface('Liip\ImagineBundle\Binary\Loader\LoaderInterface')); + } + + public function testCouldBeConstructedWithExpectedArguments() + { + return new FlysystemLoader( + ExtensionGuesser::getInstance(), + $this->flyFilesystem + ); + } + + /** + * @depends testCouldBeConstructedWithExpectedArguments + */ + public function testReturnImageContentOnFind($loader) + { + $expectedContent = file_get_contents($this->fixturesDir.'/assets/cats.jpeg'); + + $this->assertSame( + $expectedContent, + $loader->find('assets/cats.jpeg')->getContent() + ); + } + + /** + * @depends testCouldBeConstructedWithExpectedArguments + */ + public function testThrowsIfInvalidPathGivenOnFind($loader) + { + $path = 'invalid.jpeg'; + + $this->setExpectedException( + 'Liip\ImagineBundle\Exception\Binary\Loader\NotLoadableException', + sprintf('Source image "%s" not found.', $path) + ); + + $loader->find($path); + } +} diff --git a/Tests/DependencyInjection/Factory/Loader/FlysystemLoaderFactoryTest.php b/Tests/DependencyInjection/Factory/Loader/FlysystemLoaderFactoryTest.php new file mode 100644 index 000000000..46d713116 --- /dev/null +++ b/Tests/DependencyInjection/Factory/Loader/FlysystemLoaderFactoryTest.php @@ -0,0 +1,113 @@ + + */ +class FlysystemLoaderFactoryTest extends \Phpunit_Framework_TestCase +{ + public function setUp() + { + parent::setUp(); + + if (!class_exists('\League\Flysystem\Filesystem')) { + $this->markTestSkipped( + 'The league/flysystem PHP library is not available.' + ); + } + } + + public function testImplementsLoaderFactoryInterface() + { + $rc = new \ReflectionClass('Liip\ImagineBundle\DependencyInjection\Factory\Loader\FlysystemLoaderFactory'); + + $this->assertTrue($rc->implementsInterface('Liip\ImagineBundle\DependencyInjection\Factory\Loader\LoaderFactoryInterface')); + } + + public function testCouldBeConstructedWithoutAnyArguments() + { + new FlysystemLoaderFactory(); + } + + public function testReturnExpectedName() + { + $loader = new FlysystemLoaderFactory(); + + $this->assertEquals('flysystem', $loader->getName()); + } + + public function testCreateLoaderDefinitionOnCreate() + { + $container = new ContainerBuilder(); + + $loader = new FlysystemLoaderFactory(); + + $loader->create($container, 'theLoaderName', array( + 'filesystem_service' => 'flyfilesystemservice', + )); + + $this->assertTrue($container->hasDefinition('liip_imagine.binary.loader.theloadername')); + + $loaderDefinition = $container->getDefinition('liip_imagine.binary.loader.theloadername'); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\DefinitionDecorator', $loaderDefinition); + $this->assertEquals('liip_imagine.binary.loader.prototype.flysystem', $loaderDefinition->getParent()); + + $reference = $loaderDefinition->getArgument(1); + $this->assertEquals('flyfilesystemservice', "$reference"); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The child node "filesystem_service" at path "flysystem" must be configured. + */ + public function testThrowIfFileSystemServiceNotSetOnAddConfiguration() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('flysystem', 'array'); + + $resolver = new FlysystemLoaderFactory(); + $resolver->addConfiguration($rootNode); + + $this->processConfigTree($treeBuilder, array()); + } + + public function testProcessCorrectlyOptionsOnAddConfiguration() + { + $expectedService = 'theService'; + + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('flysystem', 'array'); + + $loader = new FlysystemLoaderFactory(); + $loader->addConfiguration($rootNode); + + $config = $this->processConfigTree($treeBuilder, array( + 'flysystem' => array( + 'filesystem_service' => $expectedService, + ), + )); + + $this->assertArrayHasKey('filesystem_service', $config); + $this->assertEquals($expectedService, $config['filesystem_service']); + } + + /** + * @param TreeBuilder $treeBuilder + * @param array $configs + * + * @return array + */ + protected function processConfigTree(TreeBuilder $treeBuilder, array $configs) + { + $processor = new Processor(); + + return $processor->process($treeBuilder->buildTree(), $configs); + } +} diff --git a/Tests/LiipImagineBundleTest.php b/Tests/LiipImagineBundleTest.php index 7e7a826ee..3acd257a5 100644 --- a/Tests/LiipImagineBundleTest.php +++ b/Tests/LiipImagineBundleTest.php @@ -185,6 +185,28 @@ public function testAddFilesystemLoaderFactoryOnBuild() $bundle->build($containerMock); } + public function testAddFlysystemLoaderFactoryOnBuild() + { + $extensionMock = $this->createExtensionMock(); + $extensionMock + ->expects($this->at(4)) + ->method('addLoaderFactory') + ->with($this->isInstanceOf('Liip\ImagineBundle\DependencyInjection\Factory\Loader\FlysystemLoaderFactory')) + ; + + $containerMock = $this->createContainerBuilderMock(); + $containerMock + ->expects($this->atLeastOnce()) + ->method('getExtension') + ->with('liip_imagine') + ->will($this->returnValue($extensionMock)) + ; + + $bundle = new LiipImagineBundle(); + + $bundle->build($containerMock); + } + protected function createContainerBuilderMock() { return $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder', array(), array(), '', false); diff --git a/composer.json b/composer.json index fedaef89a..565007734 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,8 @@ "twig/twig": "If you'd want to use some handy twig filters, version 1.12 or greater required", "aws/aws-sdk-php": "Add it if you'd like to use aws v2 or v3 resolver", "amazonwebservices/aws-sdk-for-php": "Add it if you'd like to use aws v1 resolver", - "monolog/monolog": "If you'd want to write logs" + "monolog/monolog": "If you'd want to write logs", + "league/flysystem": "If you'd want to use Flysystem binary loader" },