diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..319b382 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/phpunit.xml diff --git a/Controller/ImagineController.php b/Controller/ImagineController.php index 2bf9edc..94f92aa 100644 --- a/Controller/ImagineController.php +++ b/Controller/ImagineController.php @@ -2,140 +2,89 @@ namespace Avalanche\Bundle\ImagineBundle\Controller; -use Avalanche\Bundle\ImagineBundle\Imagine\CachePathResolver; -use Avalanche\Bundle\ImagineBundle\Imagine\Filter\FilterManager; -use Imagine\Image\ImagineInterface; -use Symfony\Component\HttpKernel\Util\Filesystem; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Avalanche\Bundle\ImagineBundle\Imagine\CachePathResolver; +use Avalanche\Bundle\ImagineBundle\Imagine\DataLoader\LoaderInterface; +use Avalanche\Bundle\ImagineBundle\Imagine\Filter\FilterManager; class ImagineController { /** - * @var Symfony\Component\HttpFoundation\Request + * @var Avalanche\Bundle\ImagineBundle\Imagine\DataLoader\LoaderInterface */ - private $request; + private $dataLoader; /** - * @var Avalanche\Bundle\ImagineBundle\Imagine\CachePathResolver - */ - private $cachePathResolver; - - /** - * @var Imagine\Image\ImagineInterface - */ - private $imagine; - - /** - * @var Avalanche\Bundle\ImagineBundle\Imagine\FilterManager + * @var Avalanche\Bundle\ImagineBundle\Imagine\Filter\FilterManager */ private $filterManager; /** - * @var Symfony\Component\HttpKernel\Util\Filesystem + * @var Avalanche\Bundle\ImagineBundle\Imagine\CachePathResolver */ - private $filesystem; + private $cachePathResolver; /** - * @var string + * Constructor + * + * @param Avalanche\Bundle\ImagineBundle\Imagine\DataLoader\LoaderInterface $dataLoader + * @param Avalanche\Bundle\ImagineBundle\Imagine\Filter\FilterManager $filterManager + * @param Avalanche\Bundle\ImagineBundle\Imagine\CachePathResolver $cachePathResolver */ - private $webRoot; + public function __construct(LoaderInterface $dataLoader, FilterManager $filterManager, CachePathResolver $cachePathResolver = null) + { + $this->dataLoader = $dataLoader; + $this->filterManager = $filterManager; + $this->cachePathResolver = $cachePathResolver; + } /** - * Constructs by setting $cachePathResolver + * Resolve the requested path to a local path or a Response * - * @param Symfony\Component\HttpFoundation\Request $request - * @param Avalanche\Bundle\ImagineBundle\Imagine\CachePathResolver $cachePathResolver - * @param Imagine\Image\ImagineInterface $imagine - * @param Avalanche\Bundle\ImagineBundle\Imagine\FilterManager $filterManager - * @param string $webRoot + * @param Symfony\Component\HttpFoundation\Request $request + * @param string $path + * @param string $filter + * + * @return string|Symfony\Component\HttpFoundation\Response */ - public function __construct( - Request $request, - CachePathResolver $cachePathResolver, - ImagineInterface $imagine, - FilterManager $filterManager, - Filesystem $filesystem, - $webRoot - ) + protected function resolve(Request $request, $path, $filter) { - $this->request = $request; - $this->cachePathResolver = $cachePathResolver; - $this->imagine = $imagine; - $this->filterManager = $filterManager; - $this->filesystem = $filesystem; - $this->webRoot = $webRoot; + $realPath = null; + if ($this->cachePathResolver) { + $realPath = $this->cachePathResolver->resolve($request, $path, $filter); + if (!$realPath) { + throw new NotFoundHttpException('Image doesn\'t exist'); + } + } + + return $realPath; } /** - * This action applies a given filter to a given image, saves the image and + * This action applies a given filter to a given image, + * optionally saves the image and * outputs it to the browser at the same time * + * @param Symfony\Component\HttpFoundation\Request $request * @param string $path * @param string $filter * * @return Response */ - public function filter($path, $filter) + public function filterAction(Request $request, $path, $filter) { - $path = '/'.ltrim($path, '/'); - - //TODO: find out why I need double urldecode to get a valid path - $browserPath = urldecode(urldecode($this->cachePathResolver->getBrowserPath($path, $filter))); - $basePath = $this->request->getBaseUrl(); - - if (!empty($basePath) && 0 === strpos($browserPath, $basePath)) { - $browserPath = substr($browserPath, strlen($basePath)); - } - - // if cache path cannot be determined, return 404 - if (null === $browserPath) { - throw new NotFoundHttpException('Image doesn\'t exist'); - } - - $realPath = $this->webRoot.$browserPath; - $sourcePath = $this->webRoot.$path; - - // if the file has already been cached, we're probably not rewriting - // correctly, hence make a 301 to proper location, so browser remembers - if (file_exists($realPath)) { - return new Response('', 301, array( - 'location' => $this->request->getBasePath().$browserPath - )); - } - - if (!file_exists($sourcePath)) { - throw new NotFoundHttpException(sprintf( - 'Source image not found in "%s"', $sourcePath - )); - } - - $dir = pathinfo($realPath, PATHINFO_DIRNAME); + list($actualPath, $image, $format) = $this->dataLoader->find($path); - if (!is_dir($dir)) { - if (!$this->filesystem->mkdir($dir)) { - throw new \RuntimeException(sprintf( - 'Could not create directory %s', $dir - )); - } + $realPath = $this->resolve($request, $actualPath, $filter); + if ($realPath instanceof Response) { + return $realPath; } - ob_start(); - try { - // TODO: get rid of hard-coded quality and format - $this->filterManager->get($filter) - ->apply($this->imagine->open($sourcePath)) - ->save($realPath, array('quality' => 100)) - ->show('png'); - - // TODO: add more media headers - return new Response(ob_get_clean(), 201, array( - 'content-type' => 'image/png', - )); - } catch (\Exception $e) { - ob_end_clean(); - throw $e; - } + $image = $this->filterManager->get($filter, $image, $realPath, $format); + $statusCode = $this->cachePathResolver ? 201 : 200; + $contentType = 'image/'.($format == 'jpg' ? 'jpeg' : $format); + return new Response($image, $statusCode, array('Content-Type' => $contentType)); } } diff --git a/DependencyInjection/AvalancheImagineExtension.php b/DependencyInjection/AvalancheImagineExtension.php index 74b199d..542b225 100644 --- a/DependencyInjection/AvalancheImagineExtension.php +++ b/DependencyInjection/AvalancheImagineExtension.php @@ -7,6 +7,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\DependencyInjection\Reference; class AvalancheImagineExtension extends Extension { @@ -15,47 +16,33 @@ class AvalancheImagineExtension extends Extension */ public function load(array $configs, ContainerBuilder $container) { + $config = $this->processConfiguration(new Configuration(), $configs); + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('imagine.xml'); - $config = $this->mergeConfig($configs); - - $driver = 'gd'; - - if (isset($config['driver'])) { - $driver = strtolower($config['driver']); - } - - if (!in_array($driver, array('gd', 'imagick'))) { - throw new \InvalidArgumentException('Invalid imagine driver specified'); - } - - $container->setAlias('imagine', new Alias('imagine.'.$driver)); + $container->setAlias('imagine', new Alias('imagine.'.$config['driver'])); - foreach (array('cache_prefix', 'web_root', 'filters') as $key) { - if (isset($config[$key])) { - $container->setParameter('imagine.'.$key, $config[$key]); + $cachePrefix = $config['cache_prefix'] ? '/'.trim($config['cache_prefix'], '/') : ''; + $container->setParameter('imagine.cache_prefix', $cachePrefix); + $container->setParameter('imagine.web_root', $config['web_root']); + $container->setParameter('imagine.formats', $config['formats']); + $container->setParameter('imagine.cache', $config['cache']); + foreach ($config['filters'] as $filter => $options) { + if (isset($options['path'])) { + $config['filters'][$filter]['path'] = '/'.trim($options['path'], '/'); } } - } + $container->setParameter('imagine.filters', $config['filters']); - private function mergeConfig(array $configs) - { - $config = array(); - - foreach ($configs as $cnf) { - $config = array_merge($config, $cnf); + if ($container->getParameter('imagine.cache')) { + $controller = $container->getDefinition('imagine.controller'); + $controller->addArgument(new Reference('imagine.cache.path.resolver')); } - return $config; - } - - /** - * @see Symfony\Component\DependencyInjection\Extension.ExtensionInterface::getAlias() - * @codeCoverageIgnore - */ - function getAlias() - { - return 'avalanche_imagine'; + if (!empty($config['loader'])) { + $controller = $container->getDefinition('imagine.controller'); + $controller->replaceArgument(0, new Reference($config['loader'])); + } } } diff --git a/DependencyInjection/Compiler/CreateCacheDirectoriesCompilerPass.php b/DependencyInjection/Compiler/CreateCacheDirectoriesCompilerPass.php index 80c140f..a596de6 100644 --- a/DependencyInjection/Compiler/CreateCacheDirectoriesCompilerPass.php +++ b/DependencyInjection/Compiler/CreateCacheDirectoriesCompilerPass.php @@ -9,24 +9,24 @@ class CreateCacheDirectoriesCompilerPass implements CompilerPassInterface { public function process(ContainerBuilder $container) { + if (!$container->getParameter('imagine.cache')) { + return; + } + $webRoot = $container->getParameter('imagine.web_root'); $cachePrefix = $container->getParameter('imagine.cache_prefix'); $filters = $container->getParameter('imagine.filters'); foreach ($filters as $filter => $options) { - if (isset($options['path'])) { - $dir = $webRoot.'/'.$options['path']; - } else { - $dir = $webRoot.'/'.$cachePrefix.'/'.$filter; - } + $dir = isset($options['path']) + ? $webRoot.$options['path'] + : $webRoot.$cachePrefix.'/'.$filter; - if (!is_dir($dir)) { - if (!mkdir($dir, 0777, true)) { - throw new \RuntimeException(sprintf( - 'Could not create directory for caching processed '. - 'images in "%s"', $dir - )); - } + if (!is_dir($dir) && !mkdir($dir, 0777, true)) { + throw new \RuntimeException(sprintf( + 'Could not create directory for caching processed '. + 'images in "%s"', $dir + )); } } } diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php new file mode 100644 index 0000000..7ac259a --- /dev/null +++ b/DependencyInjection/Configuration.php @@ -0,0 +1,59 @@ +root('avalanche_imagine', 'array'); + + $rootNode + ->fixXmlConfig('format', 'formats') + ->fixXmlConfig('filter', 'filters') + ->children() + ->scalarNode('driver')->defaultValue('gd') + ->validate() + ->ifTrue(function($v) { return !in_array($v, array('gd', 'imagick', 'gmagick')); }) + ->thenInvalid('Invalid imagine driver specified: %s') + ->end() + ->end() + ->scalarNode('web_root')->defaultValue('%kernel.root_dir%/../web')->end() + ->scalarNode('cache_prefix')->defaultValue('/media/cache')->end() + ->scalarNode('cache')->defaultTrue()->end() + ->scalarNode('loader')->defaultNull()->end() + ->arrayNode('formats') + ->defaultValue(array()) + ->prototype('scalar')->end() + ->end() + ->arrayNode('filters') + ->useAttributeAsKey('name') + ->prototype('array') + ->fixXmlConfig('option', 'options') + ->useAttributeAsKey('name') + ->children() + ->scalarNode('type')->end() + ->scalarNode('path')->end() + ->scalarNode('quality')->defaultValue(100)->end() + ->arrayNode('options') + ->useAttributeAsKey('name') + ->prototype('variable')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end(); + + return $treeBuilder; + } +} diff --git a/Imagine/CachePathResolver.php b/Imagine/CachePathResolver.php index 603eefe..198e05e 100644 --- a/Imagine/CachePathResolver.php +++ b/Imagine/CachePathResolver.php @@ -3,30 +3,40 @@ namespace Avalanche\Bundle\ImagineBundle\Imagine; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\RouterInterface; +use Symfony\Component\HttpKernel\Util\Filesystem; class CachePathResolver { /** - * @var string + * @var Symfony\Component\Routing\RouterInterface */ - private $webRoot; + private $router; /** - * @var Symfony\Component\Routing\RouterInterface + * @var Symfony\Component\HttpKernel\Util\Filesystem */ - private $router; + private $filesystem; + + /** + * @var string + */ + private $webRoot; /** * Constructs cache path resolver with a given web root and cache prefix * - * @param string $webRoot - * @param Symfony\Component\Routing\RouterInterface $router + * @param Symfony\Component\HttpFoundation\Request $request + * @param Symfony\Component\Routing\RouterInterface $router + * @param Symfony\Component\HttpKernel\Util\Filesystem $filesystem + * @param string $webRoot */ - public function __construct($webRoot, RouterInterface $router) + public function __construct(RouterInterface $router, Filesystem $filesystem, $webRoot) { - $this->webRoot = $webRoot; - $this->router = $router; + $this->router = $router; + $this->filesystem = $filesystem; + $this->webRoot = $webRoot; } /** @@ -55,4 +65,39 @@ public function getBrowserPath($path, $filter) return $path; } + + public function resolve(Request $request, $path, $filter) + { + //TODO: find out why I need double urldecode to get a valid path + $browserPath = urldecode(urldecode($this->getBrowserPath($path, $filter))); + $basePath = $request->getBaseUrl(); + + if (!empty($basePath) && 0 === strpos($browserPath, $basePath)) { + $browserPath = substr($browserPath, strlen($basePath)); + } + + // if cache path cannot be determined, return 404 + if (null === $browserPath) { + return false; + } + + $realPath = $this->webRoot.$browserPath; + + // if the file has already been cached, we're probably not rewriting + // correctly, hence make a 301 to proper location, so browser remembers + if (file_exists($realPath)) { + return new Response('', 301, array( + 'location' => $request->getBasePath().$browserPath + )); + } + + $dir = pathinfo($realPath, PATHINFO_DIRNAME); + if (!is_dir($dir) && !$this->filesystem->mkdir($dir)) { + throw new \RuntimeException(sprintf( + 'Could not create directory %s', $dir + )); + } + + return $realPath; + } } diff --git a/Imagine/DataLoader/FileSystemLoader.php b/Imagine/DataLoader/FileSystemLoader.php new file mode 100644 index 0000000..f448dac --- /dev/null +++ b/Imagine/DataLoader/FileSystemLoader.php @@ -0,0 +1,79 @@ +webRoot = $webRoot; + $this->formats = $formats; + } + + protected function splitPath($path) + { + $name = explode('.', $path); + if (count($name) > 1) { + $format = array_pop($name); + if (!in_array($format, $this->formats)) { + return array($path, null); + } + $name = implode('.', $name); + } else { + $format = null; + $name = $path; + } + + return array($name, $format); + } + + public function find($path) + { + $path = '/'.ltrim($path, '/'); + list($name, $targetFormat) = $this->splitPath($path); + if (!$name) { + throw new NotFoundHttpException(sprintf('Source image not found in "%s"', $path)); + } + + if (empty($targetFormat) || !file_exists($this->webRoot.$path)) { + // attempt to determine path and format + $found = false; + foreach ($this->formats as $format) { + if ($targetFormat !== $format + && file_exists($this->webRoot.$name.'.'.$format) + ) { + $path = $name.'.'.$format; + if (empty($targetFormat)) { + $targetFormat = $format; + } + $found = true; + break; + } + } + + if (!$found) { + throw new NotFoundHttpException(sprintf('Source image not found in "%s"', $path)); + } + } + + return array($path, $this->webRoot.$path, $targetFormat); + } +} diff --git a/Imagine/DataLoader/LoaderInterface.php b/Imagine/DataLoader/LoaderInterface.php new file mode 100644 index 0000000..e7405c2 --- /dev/null +++ b/Imagine/DataLoader/LoaderInterface.php @@ -0,0 +1,13 @@ +imagine = $imagine; $this->filters = $filters; $this->loaders = array(); $this->services = array(); @@ -26,7 +33,7 @@ public function addLoader($name, LoaderInterface $loader) $this->loaders[$name] = $loader; } - public function get($filter) + public function get($filter, $image, $realPath = null, $format = 'png') { if (!isset($this->filters[$filter])) { throw new InvalidArgumentException(sprintf( @@ -34,26 +41,36 @@ public function get($filter) )); } + if (is_resource($image)) { + $image = $this->imagine->load(stream_get_contents($image)); + } else { + $image = $this->imagine->open($image); + } + $options = $this->filters[$filter]; - if (!isset($options['type'])) { - throw new InvalidArgumentException(sprintf( - 'Filter type for "%s" image filter must be specified', $filter - )); - } + if (isset($options['type'])) { + if (!isset($this->loaders[$options['type']])) { + throw new InvalidArgumentException(sprintf( + 'Could not find loader for "%s" filter type', $options['type'] + )); + } - if (!isset($this->loaders[$options['type']])) { - throw new InvalidArgumentException(sprintf( - 'Could not find loader for "%s" filter type', $options['type'] - )); + if (!isset($options['options'])) { + throw new InvalidArgumentException(sprintf( + 'Options for filter type "%s" must be specified', $filter + )); + } + + $image = $this->loaders[$options['type']]->load($image, $options['options']); } - if (!isset($options['options'])) { - throw new InvalidArgumentException(sprintf( - 'Options for filter type "%s" must be specified', $filter - )); + $quality = empty($options['quality']) ? 100 : $options['quality']; + if (empty($realPath)) { + return $image->get($format, array('quality' => $quality)); } - return $this->loaders[$options['type']]->load($options['options']); + $image->save($realPath, array('quality' => $quality)); + return file_get_contents($realPath); } } diff --git a/Imagine/Filter/Loader/LoaderInterface.php b/Imagine/Filter/Loader/LoaderInterface.php index 0b8804c..6f4cd99 100644 --- a/Imagine/Filter/Loader/LoaderInterface.php +++ b/Imagine/Filter/Loader/LoaderInterface.php @@ -2,12 +2,15 @@ namespace Avalanche\Bundle\ImagineBundle\Imagine\Filter\Loader; +use Imagine\Image\ImageInterface; + interface LoaderInterface { /** + * @param Imagine\Image\ImagineInterface $image * @param array $options * - * @return Imagine\Filter\FilterInterface + * @return Imagine\Image\ImageInterface */ - function load(array $options = array()); + function load(ImageInterface $image, array $options = array()); } diff --git a/Imagine/Filter/Loader/ThumbnailFilterLoader.php b/Imagine/Filter/Loader/ThumbnailFilterLoader.php index 4bd0eee..9cc2c3f 100644 --- a/Imagine/Filter/Loader/ThumbnailFilterLoader.php +++ b/Imagine/Filter/Loader/ThumbnailFilterLoader.php @@ -3,19 +3,37 @@ namespace Avalanche\Bundle\ImagineBundle\Imagine\Filter\Loader; use Imagine\Image\Box; -use Imagine\Image\ManipulatorInterface; use Imagine\Filter\Basic\Thumbnail; +use Imagine\Image\ImageInterface; class ThumbnailFilterLoader implements LoaderInterface { - public function load(array $options = array()) + public function load(ImageInterface $image, array $options = array()) { $mode = $options['mode'] === 'inset' ? - ManipulatorInterface::THUMBNAIL_INSET : - ManipulatorInterface::THUMBNAIL_OUTBOUND; - + ImageInterface::THUMBNAIL_INSET : + ImageInterface::THUMBNAIL_OUTBOUND; list($width, $height) = $options['size']; - return new Thumbnail(new Box($width, $height), $mode); + $size = $image->getSize(); + $origWidth = $size->getWidth(); + $origHeight = $size->getHeight(); + + if (null === $width || null === $height) { + if (null === $height) { + $height = (int)(($width / $origWidth) * $origHeight); + } else if (null === $width) { + $width = (int)(($height / $origHeight) * $origWidth); + } + } + + if ((!empty($options['allow_upscale']) && $origWidth !== $width && $origHeight !== $height) + || ($origWidth > $width || $origHeight > $height) + ) { + $filter = new Thumbnail(new Box($width, $height), $mode); + $image = $filter->apply($image); + } + + return $image; } } diff --git a/README.md b/README.md index 6f830c8..96a99ea 100644 --- a/README.md +++ b/README.md @@ -156,8 +156,11 @@ The default configuration for the bundle looks like this: ``` yaml avalanche_imagine: web_root: %kernel.root_dir%/../web - cache_prefix: media/cache + cache_prefix: /media/cache + cache: true + loader: ~ driver: gd + formats: [] filters: [] ``` @@ -174,12 +177,20 @@ There are several configuration options available: as to not clutter your web root with cached images. For example by default, the images would be written to the `web/media/cache/` directory. - default: `media/cache` + default: `/media/cache` + + - `cache` - if to cache the generated image in the local file system + + - `loader` - service id for a custom loader + + default: null (which means the standard filesystem loader is used) - `driver` - one of the three drivers: `gd`, `imagick`, `gmagick` default: `gd` + - `formats` - optional list of image formats to which images may be converted to. + - `filters` - specify the filters that you want to define and use Each filter that you specify have the following options: @@ -187,6 +198,7 @@ Each filter that you specify have the following options: - `type` - determine the type of filter to be used, refer to *Filters* section for more information - `options` - options that should be passed to the specific filter type - `path` - override the global `cache_prefix` and replace it with this path + - `quality` - override the default quality of 100 for the generated images ## Built-in Filters diff --git a/Resources/config/imagine.xml b/Resources/config/imagine.xml index 36cd5a6..9a55f8b 100644 --- a/Resources/config/imagine.xml +++ b/Resources/config/imagine.xml @@ -5,16 +5,11 @@ - - - %kernel.root_dir%/../web - media/cache - - Avalanche\Bundle\ImagineBundle\Imagine\Filter\FilterManager Avalanche\Bundle\ImagineBundle\Imagine\CachePathResolver + Avalanche\Bundle\ImagineBundle\Imagine\DataLoader\FileSystemLoader @@ -46,23 +41,26 @@ - %imagine.web_root% + + %imagine.web_root% + %imagine.filters% + + %imagine.web_root% + %imagine.formats% + + - - - - + + - - %imagine.web_root% diff --git a/Routing/ImagineLoader.php b/Routing/ImagineLoader.php index 1b894ea..ab2fda7 100644 --- a/Routing/ImagineLoader.php +++ b/Routing/ImagineLoader.php @@ -25,16 +25,18 @@ public function supports($resource, $type = null) public function load($resource, $type = null) { - $requirements = array('_method' => 'GET', 'filter' => '[A-z0-9_\-]*', 'path' => '.+'); - $defaults = array('_controller' => 'imagine.controller:filter'); + $requirements = array('_method' => 'GET', 'filter' => '[A-z0-9_\-]*', 'path' => '.+'); + $defaults = array('_controller' => 'imagine.controller:filterAction'); $routes = new RouteCollection(); if (count($this->filters) > 0) { foreach ($this->filters as $filter => $options) { if (isset($options['path'])) { - $pattern = '/'.trim($options['path'], '/').'/{path}'; + $pattern = $options['path'].'/{path}'; + } elseif ('' !== $filter) { + $pattern = $this->cachePrefix.'/'.$filter.'/{path}'; } else { - $pattern = '/'.trim($this->cachePrefix, '/').'/'.$filter.'/{path}'; + $pattern = $this->cachePrefix.'/'.'{path}'; } $routes->add('_imagine_'.$filter, new Route( diff --git a/Tests/DependencyInjection/AvalancheImagineExtensionTest.php b/Tests/DependencyInjection/AvalancheImagineExtensionTest.php new file mode 100644 index 0000000..cb142b5 --- /dev/null +++ b/Tests/DependencyInjection/AvalancheImagineExtensionTest.php @@ -0,0 +1,153 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Avalanche\Bundle\ImagineBundle\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Avalanche\Bundle\ImagineBundle\DependencyInjection\AvalancheImagineExtension; +use Symfony\Component\Yaml\Parser; +use Symfony\Component\DependencyInjection\Reference; + +class AvalancheImagineExtensionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Symfony\Component\DependencyInjection\ContainerBuilder + */ + protected $containerBuilder; + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testUserLoadThrowsExceptionUnlessDriverIsValid() + { + $loader = new AvalancheImagineExtension(); + $config = array('driver' => 'foo'); + $loader->load(array($config), new ContainerBuilder()); + } + + public function testLoadWithDefaults() + { + $this->createEmptyConfiguration(); + + $this->assertParameter(true, 'imagine.cache'); + $this->assertAlias('imagine.gd', 'imagine'); + $this->assertHasDefinition('imagine.controller'); + $this->assertDICConstructorArguments( + $this->containerBuilder->getDefinition('imagine.controller'), + array(new Reference('imagine.loader.filesystem'), new Reference('imagine.filter.manager'), new Reference('imagine.cache.path.resolver')) + ); + } + + public function testLoad() + { + $this->createFullConfiguration(); + + $this->assertParameter(false, 'imagine.cache'); + $this->assertAlias('imagine.imagick', 'imagine'); + $this->assertHasDefinition('imagine.controller'); + $this->assertDICConstructorArguments( + $this->containerBuilder->getDefinition('imagine.controller'), + array(new Reference('acme_imagine.loader'), new Reference('imagine.filter.manager')) + ); + } + + /** + * @return ContainerBuilder + */ + protected function createEmptyConfiguration() + { + $this->containerBuilder = new ContainerBuilder(); + $loader = new AvalancheImagineExtension(); + $loader->load(array(array()), $this->containerBuilder); + $this->assertTrue($this->containerBuilder instanceof ContainerBuilder); + } + + /** + * @return ContainerBuilder + */ + protected function createFullConfiguration() + { + $this->containerBuilder = new ContainerBuilder(); + $loader = new AvalancheImagineExtension(); + $loader->load(array($this->getFullConfig()), $this->containerBuilder); + $this->assertTrue($this->containerBuilder instanceof ContainerBuilder); + } + + protected function getFullConfig() + { + $yaml = <<parse($yaml); + } + + private function assertAlias($value, $key) + { + $this->assertEquals($value, (string) $this->containerBuilder->getAlias($key), sprintf('%s alias is correct', $key)); + } + + private function assertParameter($value, $key) + { + $this->assertEquals($value, $this->containerBuilder->getParameter($key), sprintf('%s parameter is correct', $key)); + } + + private function assertHasDefinition($id) + { + $this->assertTrue(($this->containerBuilder->hasDefinition($id) ?: $this->containerBuilder->hasAlias($id))); + } + + private function assertNotHasDefinition($id) + { + $this->assertFalse(($this->containerBuilder->hasDefinition($id) ?: $this->containerBuilder->hasAlias($id))); + } + + private function assertDICConstructorArguments($definition, $args) + { + $this->assertEquals($args, $definition->getArguments(), "Expected and actual DIC Service constructor arguments of definition '".$definition->getClass()."' don't match."); + } + + protected function tearDown() + { + unset($this->containerBuilder); + } +} diff --git a/Tests/bootstrap.php b/Tests/bootstrap.php new file mode 100644 index 0000000..b52ec5c --- /dev/null +++ b/Tests/bootstrap.php @@ -0,0 +1,26 @@ +registerNamespace('Symfony', SYMFONY_SRC_DIR); +$loader->registerNamespace('Imagine', IMAGINE_SRC_DIR); +$loader->register(); diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..1380396 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,36 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + + + + + + + + + + +