Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Flysystem resolver #715

Merged
merged 1 commit into from
Jul 12, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .scrutinizer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ before_commands:
# - "composer install --prefer-source --dev"

filter:
excluded_paths: [vendor/*, tests/*, bin/*]
excluded_paths: [vendor/*, Tests/*, bin/*]

tools:
php_sim: true
Expand All @@ -24,5 +24,5 @@ tools:
excluded_dirs: [vendor]
php_pdepend:
enabled: true
excluded_dirs: [vendor, tests, bin]
excluded_dirs: [vendor, Tests, bin]
# php_hhvm: true
52 changes: 52 additions & 0 deletions DependencyInjection/Factory/Resolver/FlysystemResolverFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace Liip\ImagineBundle\DependencyInjection\Factory\Resolver;

use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Reference;

class FlysystemResolverFactory implements ResolverFactoryInterface
{
/**
* {@inheritdoc}
*/
public function create(ContainerBuilder $container, $resolverName, array $config)
{
$resolverDefinition = new DefinitionDecorator('liip_imagine.cache.resolver.prototype.flysystem');
$resolverDefinition->replaceArgument(0, new Reference($config['filesystem_service']));
$resolverDefinition->replaceArgument(2, $config['root_url']);
$resolverDefinition->replaceArgument(3, $config['cache_prefix']);
$resolverDefinition->addTag('liip_imagine.cache.resolver', array(
'resolver' => $resolverName,
));
$resolverId = 'liip_imagine.cache.resolver.'.$resolverName;

$container->setDefinition($resolverId, $resolverDefinition);

return $resolverId;
}

/**
* {@inheritdoc}
*/
public function getName()
{
return 'flysystem';
}

/**
* {@inheritdoc}
*/
public function addConfiguration(ArrayNodeDefinition $builder)
{
$builder
->children()
->scalarNode('filesystem_service')->isRequired()->cannotBeEmpty()->end()
->scalarNode('cache_prefix')->defaultValue(null)->end()
->scalarNode('root_url')->isRequired()->cannotBeEmpty()->end()
->end()
;
}
}
152 changes: 152 additions & 0 deletions Imagine/Cache/Resolver/FlysystemResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<?php

namespace Liip\ImagineBundle\Imagine\Cache\Resolver;

use League\Flysystem\Filesystem;
use Liip\ImagineBundle\Binary\BinaryInterface;
use Liip\ImagineBundle\Exception\Imagine\Cache\Resolver\NotResolvableException;
use Symfony\Component\Routing\RequestContext;

class FlysystemResolver implements ResolverInterface
{
/**
* @var Filesystem
*/
protected $flysystem;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$filesystem

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to keep the name flysystem. Is this a major problem?


/**
* @var RequestContext
*/
protected $requestContext;

/**
* @var string
*/
protected $webRoot;

/**
* @var string
*/
protected $cachePrefix;

/**
* @var string
*/
protected $cacheRoot;

/**
* FlysystemResolver constructor.
*
* @param Filesystem $flysystem
* @param RequestContext $requestContext
* @param $rootUrl
* @param string $cachePrefix
*/
public function __construct(
Filesystem $flysystem,
RequestContext $requestContext,
$rootUrl,
$cachePrefix = 'media/cache'
) {
$this->flysystem = $flysystem;
$this->requestContext = $requestContext;

$this->webRoot = rtrim($rootUrl, '/');
$this->cachePrefix = ltrim(str_replace('//', '/', $cachePrefix), '/');
$this->cacheRoot = $this->cachePrefix;
}

/**
* Checks whether the given path is stored within this Resolver.
*
* @param string $path
* @param string $filter
*
* @return bool
*/
public function isStored($path, $filter)
{
return $this->flysystem->has($this->getFilePath($path, $filter));
}

/**
* {@inheritdoc}
*/
protected function getFilePath($path, $filter)
{
return $this->getFileUrl($path, $filter);
}

/**
* {@inheritdoc}
*/
protected function getFileUrl($path, $filter)
{
// crude way of sanitizing URL scheme ("protocol") part
$path = str_replace('://', '---', $path);

return $this->cachePrefix.'/'.$filter.'/'.ltrim($path, '/');
}

/**
* Resolves filtered path for rendering in the browser.
*
* @param string $path The path where the original file is expected to be.
* @param string $filter The name of the imagine filter in effect.
*
* @return string The absolute URL of the cached image.
*
* @throws NotResolvableException
*/
public function resolve($path, $filter)
{
return sprintf(
'%s/%s',
$this->webRoot,
$this->getFileUrl($path, $filter)
);
}

/**
* Stores the content of the given binary.
*
* @param BinaryInterface $binary The image binary to store.
* @param string $path The path where the original file is expected to be.
* @param string $filter The name of the imagine filter in effect.
*/
public function store(BinaryInterface $binary, $path, $filter)
{
$this->flysystem->put(
$this->getFilePath($path, $filter),
$binary->getContent()
);
}

/**
* @param string[] $paths The paths where the original files are expected to be.
* @param string[] $filters The imagine filters in effect.
*/
public function remove(array $paths, array $filters)
{
if (empty($paths) && empty($filters)) {
return;
}

if (empty($paths)) {
foreach ($filters as $filter) {
$filterCacheDir = $this->cacheRoot.'/'.$filter;
$this->flysystem->deleteDir($filterCacheDir);
}

return;
}

foreach ($paths as $path) {
foreach ($filters as $filter) {
if ($this->flysystem->has($this->getFilePath($path, $filter))) {
$this->flysystem->delete($this->getFilePath($path, $filter));
}
}
}
}
}
2 changes: 2 additions & 0 deletions LiipImagineBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
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\FlysystemResolverFactory;
use Liip\ImagineBundle\DependencyInjection\Factory\Resolver\WebPathResolverFactory;
use Liip\ImagineBundle\DependencyInjection\LiipImagineExtension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
Expand All @@ -34,6 +35,7 @@ public function build(ContainerBuilder $container)

$extension->addResolverFactory(new WebPathResolverFactory());
$extension->addResolverFactory(new AwsS3ResolverFactory());
$extension->addResolverFactory(new FlysystemResolverFactory());

$extension->addLoaderFactory(new StreamLoaderFactory());
$extension->addLoaderFactory(new FileSystemLoaderFactory());
Expand Down
10 changes: 9 additions & 1 deletion Resources/config/imagine.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
<parameter key="liip_imagine.cache.resolver.no_cache_web_path.class">Liip\ImagineBundle\Imagine\Cache\Resolver\NoCacheWebPathResolver</parameter>
<parameter key="liip_imagine.cache.resolver.aws_s3.class">Liip\ImagineBundle\Imagine\Cache\Resolver\AwsS3Resolver</parameter>
<parameter key="liip_imagine.cache.resolver.cache.class">Liip\ImagineBundle\Imagine\Cache\Resolver\CacheResolver</parameter>
<parameter key="liip_imagine.cache.resolver.flysystem.class">Liip\ImagineBundle\Imagine\Cache\Resolver\FlysystemResolver</parameter>
<parameter key="liip_imagine.cache.resolver.proxy.class">Liip\ImagineBundle\Imagine\Cache\Resolver\ProxyResolver</parameter>

<!-- Form types -->
Expand Down Expand Up @@ -208,7 +209,7 @@
<argument><!-- will be injected by StreamLoaderFactory --></argument>
<argument><!-- will be injected by StreamLoaderFactory --></argument>
</service>

<service id="liip_imagine.binary.loader.prototype.flysystem" class="%liip_imagine.binary.loader.flysystem.class%" abstract="true">
<argument type="service" id="liip_imagine.extension_guesser" />
<argument><!-- will be injected by FlysystemLoaderFactory --></argument>
Expand Down Expand Up @@ -236,6 +237,13 @@
<argument><!-- will be injected by a ResolverFactory --></argument>
</service>

<service id="liip_imagine.cache.resolver.prototype.flysystem" class="%liip_imagine.cache.resolver.flysystem.class%" public="true" abstract="true">
<argument><!-- will be injected by a ResolverFactory --></argument>
<argument type="service" id="router.request_context" />
<argument><!-- will be injected by a ResolverFactory --></argument>
<argument><!-- will be injected by a ResolverFactory --></argument>
</service>

<service id="liip_imagine.cache.resolver.prototype.proxy" class="%liip_imagine.cache.resolver.proxy.class%" public="true" abstract="true">
<argument><!-- will be injected by AwsS3ResolverFactory --></argument>
<argument><!-- will be injected by AwsS3ResolverFactory --></argument>
Expand Down
63 changes: 63 additions & 0 deletions Resources/doc/cache-resolver/flysystem.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
FlysystemResolver
=================

This resolver lets you load images onto `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.

Create resolver
---------------

.. code-block:: yaml

liip_imagine:
resolvers:
profile_photos:
flysystem:
filesystem_service: oneup_flysystem.profile_photos_filesystem
root_url: http://images.example.com
cache_prefix: media/cache
oneup_flysystem:
adapters:
profile_photos:
local:
directory: "path/to/profile/photos"

filesystems:
profile_photos:
adapter: profile_photos

There are several configuration options available:

* ``root_url`` - must be a valid url to the target system the flysystem adapter
points to. This is used to determine how the url should be generated upon request.
Default value: ``null``
* ``cache_prefix`` - this is used for the image path generation. This will be the
prefix inside the given Flysystem.
Default value: ``media/cache``

Usage
-----

.. code-block:: yaml

liip_imagine:
cache: profile_photos

Usage on a specific filter
--------------------------

.. code-block:: yaml

liip_imagine:
filter_sets:
cache: ~
my_thumb:
cache: profile_photos
quality: 75
filters:
thumbnail: { size: [120, 90], mode: outbound }
Loading