commit ae7440f4d70f3606d993f9a91c2f7f075d7adaf7 Author: Antoine Hérault Date: Sun May 8 23:04:37 2011 +0200 Initial commit diff --git a/DependencyInjection/Compiler/AdapterFactoryManagerPass.php b/DependencyInjection/Compiler/AdapterFactoryManagerPass.php new file mode 100644 index 0000000..6303a63 --- /dev/null +++ b/DependencyInjection/Compiler/AdapterFactoryManagerPass.php @@ -0,0 +1,35 @@ + + */ +class AdapterFactoryManagerPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('knplabs_gaufrette.adapter_factory_manager')) { + return; + } + + $definition = $container->getDefinition('knplabs_gaufrette.adapter_factory_manager'); + + $calls = $definition->getMethodCalls(); + $definition->setMethodCalls(array()); + + foreach ($container->findTaggedServiceIds('gaufrette.adapter_factory') as $id => $attributes) { + if (!empty($attributes['type'])) { + $definition->addMethodCall('set', array($attributes['type'], new Reference($id))); + } + } + + $definition->setMethodCalls(array_merge($definition->getMethodCalls(), $calls)); + } +} diff --git a/DependencyInjection/Compiler/AdapterManagerPass.php b/DependencyInjection/Compiler/AdapterManagerPass.php new file mode 100644 index 0000000..37e1060 --- /dev/null +++ b/DependencyInjection/Compiler/AdapterManagerPass.php @@ -0,0 +1,34 @@ + + */ +class AdapterManagerPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('knplabs_gaufrette.adapter_manager')) { + return; + } + + $definition = $container->getDefinition('knplabs_gaufrette.adapter_manager'); + $calls = $definition->getMethodCalls(); + $definition->setMethodCalls(array()); + + foreach ($container->findTaggedServiceIds('gaufrette.adapter') as $id => $attributes) { + if (!empty($attributes['alias'])) { + $definition->addMethodCall('set', array($attributes['alias'], new Reference($id))); + } + } + + $definition->setMethodCalls(array_merge($definition->getMethodCalls(), $calls)); + } +} diff --git a/DependencyInjection/Factory/AdapterFactoryInterface.php b/DependencyInjection/Factory/AdapterFactoryInterface.php new file mode 100644 index 0000000..a49d83a --- /dev/null +++ b/DependencyInjection/Factory/AdapterFactoryInterface.php @@ -0,0 +1,39 @@ + + */ +interface AdapterFactoryInterface +{ + /** + * Creates the adapter, registers it and returns its id + * + * @param ContainerBuilder $container + * @param string $id + * @param array $config + * + * @return string The Adapter service id in the DIC + */ + function create(ContainerBuilder $container, $id, array $config); + + /** + * Returns the key for the factory configuration + * + * @return string + */ + function getKey(); + + /** + * Adds configuration nodes for the factory + * + * @param NodeBuilder $builder + */ + function addConfiguration(NodeDefinition $builder); +} diff --git a/DependencyInjection/Factory/InMemoryAdapterFactory.php b/DependencyInjection/Factory/InMemoryAdapterFactory.php new file mode 100644 index 0000000..4bf578b --- /dev/null +++ b/DependencyInjection/Factory/InMemoryAdapterFactory.php @@ -0,0 +1,60 @@ + + */ +class InMemoryAdapterFactory implements AdapterFactoryInterface +{ + /** + * {@inheritDoc} + */ + public function create(ContainerBuilder $container, $id, array $config) + { + $adapter = sprintf('knplabs_gaufrette.adapter.local.%s', $id); + + $container + ->setDefinition($adapter, new DefinitionDecorator('knplabs_gaufrette.adapter.in_memory')) + ->replaceArgument(0, $config['files']) + ; + + return $adapter; + } + + /** + * {@inheritDoc} + */ + public function getKey() + { + return 'in_memory'; + } + + /** + * {@inheritDoc} + */ + public function addConfiguration(NodeDefinition $node) + { + $node + ->children() + ->arrayNode('files') + ->fixXmlConfig('file') + ->useAttributeAsKey('filename') + ->prototype('array') + ->children() + ->scalarNode('content')->end() + ->scalarNode('checksum')->end() + ->scalarNode('mtime')->end() + ->end() + ->end() + ->end() + ->end() + ; + } +} diff --git a/DependencyInjection/Factory/LocalAdapterFactory.php b/DependencyInjection/Factory/LocalAdapterFactory.php new file mode 100644 index 0000000..5d1fb15 --- /dev/null +++ b/DependencyInjection/Factory/LocalAdapterFactory.php @@ -0,0 +1,52 @@ + + */ +class LocalAdapterFactory implements AdapterFactoryInterface +{ + /** + * {@inheritDoc} + */ + public function create(ContainerBuilder $container, $id, array $config) + { + $adapter = sprintf('knplabs_gaufrette.adapter.local.%s', $id); + + $container + ->setDefinition($adapter, new DefinitionDecorator('knplabs_gaufrette.adapter.local')) + ->replaceArgument(0, $config['directory']) + ->replaceArgument(1, $config['create']) + ; + + return $adapter; + } + + /** + * {@inheritDoc} + */ + public function getKey() + { + return 'local'; + } + + /** + * {@inheritDoc} + */ + public function addConfiguration(NodeDefinition $node) + { + $node + ->children() + ->scalarNode('directory')->isRequired()->end() + ->booleanNode('create')->defaultTrue()->end() + ->end() + ; + } +} diff --git a/DependencyInjection/Factory/SafeLocalAdapterFactory.php b/DependencyInjection/Factory/SafeLocalAdapterFactory.php new file mode 100644 index 0000000..49540d5 --- /dev/null +++ b/DependencyInjection/Factory/SafeLocalAdapterFactory.php @@ -0,0 +1,52 @@ + + */ +class SafeLocalAdapterFactory implements AdapterFactoryInterface +{ + /** + * {@inheritDoc} + */ + public function create(ContainerBuilder $container, $id, array $config) + { + $adapter = sprintf('knplabs_gaufrette.adapter.safe_local.%s', $id); + + $container + ->setDefinition($adapter, new DefinitionDecorator('knplabs_gaufrette.adapter.safe_local')) + ->replaceArgument(0, $config['directory']) + ->replaceArgument(1, $config['create']) + ; + + return $adapter; + } + + /** + * {@inheritDoc} + */ + public function getKey() + { + return 'safe-local'; + } + + /** + * {@inheritDoc} + */ + public function addConfiguration(NodeDefinition $node) + { + $node + ->children() + ->scalarNode('directory')->isRequired()->end() + ->booleanNode('create')->defaultTrue()->end() + ->end() + ; + } +} diff --git a/DependencyInjection/Factory/ServiceAdapterFactory.php b/DependencyInjection/Factory/ServiceAdapterFactory.php new file mode 100644 index 0000000..aefe533 --- /dev/null +++ b/DependencyInjection/Factory/ServiceAdapterFactory.php @@ -0,0 +1,42 @@ + + */ +class ServiceAdapterFactory implements AdapterFactoryInterface +{ + /** + * {@inheritDoc} + */ + public function create(ContainerBuilder $container, $id, array $config) + { + return $config['id']; + } + + /** + * {@inheritDoc} + */ + public function getKey() + { + return 'service'; + } + + /** + * {@inheritDoc} + */ + public function addConfiguration(NodeDefinition $builder) + { + $builder + ->children() + ->scalarNode('id')->isRequired()->end() + ->end() + ; + } +} diff --git a/DependencyInjection/FactoryConfiguration.php b/DependencyInjection/FactoryConfiguration.php new file mode 100644 index 0000000..e9a0f9d --- /dev/null +++ b/DependencyInjection/FactoryConfiguration.php @@ -0,0 +1,38 @@ + + */ +class FactoryConfiguration implements ConfigurationInterface +{ + /** + * Generates the configuration tree builder + * + * @return TreeBuilder + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + + $treeBuilder + ->root('knplabs_gaufrette') + ->ignoreExtraKeys() + ->fixXmlConfig('factory', 'factories') + ->children() + ->arrayNode('factories') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ; + + return $treeBuilder; + } +} diff --git a/DependencyInjection/KnplabsGaufretteExtension.php b/DependencyInjection/KnplabsGaufretteExtension.php new file mode 100644 index 0000000..2710bbd --- /dev/null +++ b/DependencyInjection/KnplabsGaufretteExtension.php @@ -0,0 +1,117 @@ + + */ +class KnplabsGaufretteExtension extends Extension +{ + private $factories = null; + + /** + * Loads the extension + * + * @param array $configs + * @param ContainerBuilder $container + */ + public function load(array $configs, ContainerBuilder $container) + { + $processor = new Processor(); + + // first assemble the adapter factories + $factoryConfig = new FactoryConfiguration(); + $config = $processor->processConfiguration($factoryConfig, $configs); + $factories = $this->createAdapterFactories($config, $container); + + // then normalize the configs + $mainConfig = new MainConfiguration($factories); + $config = $processor->processConfiguration($mainConfig, $configs); + + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('gaufrette.xml'); + + foreach ($config['adapters'] as $name => $adapter) { + $adapters[$name] = $this->createAdapter($name, $adapter, $container, $factories); + } + + foreach ($config['filesystems'] as $name => $filesystem) { + $this->createFilesystem($name, $filesystem, $container, $adapters); + } + } + + private function createAdapter($name, array $config, ContainerBuilder $container, array $factories) + { + $adapter = null; + foreach ($config as $key => $adapter) { + if (array_key_exists($key, $factories)) { + return $factories[$key]->create($container, $name, $adapter); + } + } + + throw new \LogicException(sprintf('The adapter \'%s\' is not configured.', $name)); + } + + private function createFilesystem($name, array $config, ContainerBuilder $container, array $adapters) + { + if (!array_key_exists($config['adapter'], $adapters)) { + throw new \LogicException(sprintf('The adapter \'%s\' is not defined.', $config['adapter'])); + } + + $adapter = $adapters[$config['adapter']]; + $id = sprintf('gaufrette.%s_filesystem', $name); + + $container + ->setDefinition($id, new DefinitionDecorator('knplabs_gaufrette.filesystem')) + ->replaceArgument(0, new Reference($adapter)) + ; + + if (!empty($config['alias'])) { + $container->setAlias($config['alias'], $id); + } + } + + /** + * Creates the adapter factories + * + * @param array $config + * @param ContainerBuilder $container + */ + private function createAdapterFactories($config, ContainerBuilder $container) + { + if (null !== $this->factories) { + return $this->factories; + } + + // load bundled adapter factories + $tempContainer = new ContainerBuilder(); + $parameterBag = $container->getParameterBag(); + $loader = new XmlFileLoader($tempContainer, new FileLocator(__DIR__.'/../Resources/config')); + + $loader->load('adapter_factories.xml'); + + // load user-created adapter factories + foreach ($config['factories'] as $factory) { + $loader->load($parameterBag->resolveValue($factory)); + } + + $services = $tempContainer->findTaggedServiceIds('gaufrette.adapter.factory'); + $factories = array(); + foreach (array_keys($services) as $id) { + $factory = $tempContainer->get($id); + $factories[str_replace('-', '_', $factory->getKey())] = $factory; + } + + return $this->factories = $factories; + } +} diff --git a/DependencyInjection/MainConfiguration.php b/DependencyInjection/MainConfiguration.php new file mode 100644 index 0000000..2e6187c --- /dev/null +++ b/DependencyInjection/MainConfiguration.php @@ -0,0 +1,87 @@ + + */ +class MainConfiguration implements ConfigurationInterface +{ + private $factories; + + /** + * Constructor + * + * @param array $factories + */ + public function __construct(array $factories) + { + $this->factories = $factories; + } + + /** + * Generates the configuration tree builder + * + * @return TreeBuilder + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('knplabs_gaufrette'); + + $this->addAdaptersSection($rootNode, $this->factories); + $this->addFilesystemsSection($rootNode); + + $rootNode + // add a faux-entry for factories, so that no validation error is thrown + ->fixXmlConfig('factory', 'factories') + ->children() + ->arrayNode('factories')->ignoreExtraKeys()->end() + ->end() + ; + + return $treeBuilder; + } + + private function addAdaptersSection(ArrayNodeDefinition $node, array $factories) + { + $adapterNodeBuilder = $node + ->fixXmlConfig('adapter') + ->children() + ->arrayNode('adapters') + ->useAttributeAsKey('name') + ->prototype('array') + ->performNoDeepMerging() + ->children() + ; + + foreach ($factories as $name => $factory) { + $factoryNode = $adapterNodeBuilder->arrayNode($name)->canBeUnset(); + + $factory->addConfiguration($factoryNode); + } + } + + private function addFilesystemsSection(ArrayNodeDefinition $node) + { + $node + ->fixXmlConfig('filesystem') + ->children() + ->arrayNode('filesystems') + ->useAttributeAsKey('name') + ->prototype('array') + ->children() + ->scalarNode('adapter')->isRequired()->end() + ->scalarNode('alias')->defaultNull()->end() + ->end() + ->end() + ->end() + ; + } +} diff --git a/KnplabsGaufretteBundle.php b/KnplabsGaufretteBundle.php new file mode 100644 index 0000000..7217341 --- /dev/null +++ b/KnplabsGaufretteBundle.php @@ -0,0 +1,15 @@ + + */ +class KnplabsGaufretteBundle extends Bundle +{ +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..48106fb --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2011 by 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. diff --git a/README.markdown b/README.markdown new file mode 100644 index 0000000..b7f96e1 --- /dev/null +++ b/README.markdown @@ -0,0 +1,187 @@ +Gaufrette Bundle +================ + +Provides a [Gaufrette][gaufrette-homepage] integration for your Symfony projects. + +Installation +------------ + +## Prérequisites + +As this bundle is an integration for Symfony of the [Gaufrette][gaufrette-homepage] library, it requires you to first install [Gaufrette][gaufrette-homepage] in a Symfony project. + +## Download the bundle + +You can download an archive of the bundle and unpack it in the `vendor/bundles/Knplabs/Bundle/GaufretteBundle` directory of your application. + +If you are versioning your project with git, you had better to embed it as a submodule: + + $ git submodule add https://github.com/knplabs/GaufretteBundle.git vendor/bundles/Knplabs/Bundle/GaufretteBundle + +## Add the namespace in the autoloader + +If the `Knplabs` namespace is not already defined in your autoloader, you must add it: + +``` php +registerNamespaces(array( + + 'Knplabs' => __DIR__.'/../vendor/bundles' + + // ... + +)); +``` + +## Register the bundle + +You must register the bundle in your kernel: + +``` php + + + + + + Knplabs\Bundle\GaufretteBundle\DependencyInjection\Factory\InMemoryAdapterFactory + Knplabs\Bundle\GaufretteBundle\DependencyInjection\Factory\ServiceAdapterFactory + Knplabs\Bundle\GaufretteBundle\DependencyInjection\Factory\LocalAdapterFactory + Knplabs\Bundle\GaufretteBundle\DependencyInjection\Factory\SafeLocalAdapterFactory + + + + + + + + + + + + + + + + + + diff --git a/Resources/config/gaufrette.xml b/Resources/config/gaufrette.xml new file mode 100644 index 0000000..dc9900f --- /dev/null +++ b/Resources/config/gaufrette.xml @@ -0,0 +1,31 @@ + + + + + + Gaufrette\Filesystem\Filesystem + Gaufrette\Filesystem\Adapter\InMemory + Gaufrette\Filesystem\Adapter\Local + Gaufrette\Filesystem\Adapter\SafeLocal + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/views/Default/index.html.twig b/Resources/views/Default/index.html.twig new file mode 100644 index 0000000..05a682b --- /dev/null +++ b/Resources/views/Default/index.html.twig @@ -0,0 +1 @@ +Hello! \ No newline at end of file diff --git a/Tests/FunctionalTest.php b/Tests/FunctionalTest.php new file mode 100644 index 0000000..26620ca --- /dev/null +++ b/Tests/FunctionalTest.php @@ -0,0 +1,66 @@ +cacheDir = __DIR__.'/Resources/cache'; + if (file_exists($this->cacheDir)) { + $filesystem = new Filesystem(); + $filesystem->remove($this->cacheDir); + } + + mkdir($this->cacheDir, 0777, true); + } + + /** + * @dataProvider getConfigurationData + */ + public function testConfiguration($env, array $filesystems) + { + $kernel = new TestKernel($env, false); + $kernel->boot(); + + $container = $kernel->getContainer(); + + foreach ($filesystems as $id => $adapterClass) { + $this->assertTrue($container->has($id), sprintf('Filesystem service \'%s\' exists.', $id)); + + $filesystem = $container->get($id); + $this->assertInstanceOf('Gaufrette\Filesystem\Filesystem', $filesystem); + + $reflProperty = new \ReflectionProperty($filesystem, 'adapter'); + $reflProperty->setAccessible(true); + + $adapter = $reflProperty->getValue($filesystem); + + $reflProperty->setAccessible(false); + + $this->assertInstanceOf($adapterClass, $adapter); + } + } + + public function getConfigurationData() + { + return array( + array( + 'dev', + array( + 'gaufrette.foo_filesystem' => 'Gaufrette\Filesystem\Adapter\Local', + 'foo_filesystem' => 'Gaufrette\Filesystem\Adapter\Local', + ) + ), + array( + 'test', + array( + 'gaufrette.foo_filesystem' => 'Gaufrette\Filesystem\Adapter\InMemory', + 'foo_filesystem' => 'Gaufrette\Filesystem\Adapter\InMemory', + ) + ) + ); + } +} diff --git a/Tests/Resources/cache/test/ResourcesTestProjectContainer.php b/Tests/Resources/cache/test/ResourcesTestProjectContainer.php new file mode 100644 index 0000000..121fd2c --- /dev/null +++ b/Tests/Resources/cache/test/ResourcesTestProjectContainer.php @@ -0,0 +1,69 @@ +parameters = $this->getDefaultParameters(); + $this->services = + $this->scopedServices = + $this->scopeStacks = array(); + $this->set('service_container', $this); + $this->scopes = array(); + $this->scopeChildren = array(); + } + protected function getGaufrette_FooFilesystemService() + { + return $this->services['gaufrette.foo_filesystem'] = new \Gaufrette\Filesystem\Filesystem($this->get('knplabs_gaufrette.adapter.local.foo')); + } + protected function getKnplabsGaufrette_Adapter_Local_FooService() + { + return $this->services['knplabs_gaufrette.adapter.local.foo'] = new \Gaufrette\Filesystem\Adapter\InMemory(array()); + } + protected function getFooFilesystemService() + { + return $this->get('gaufrette.foo_filesystem'); + } + public function getParameter($name) + { + $name = strtolower($name); + if (!array_key_exists($name, $this->parameters)) { + throw new \InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + return $this->parameters[$name]; + } + public function hasParameter($name) + { + return array_key_exists(strtolower($name), $this->parameters); + } + public function setParameter($name, $value) + { + throw new \LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + protected function getDefaultParameters() + { + return array( + 'kernel.root_dir' => '/home/antoine/htdocs/symfony/vendor/bundles/Knplabs/Bundle/GaufretteBundle/Tests/Resources', + 'kernel.environment' => 'test', + 'kernel.debug' => false, + 'kernel.name' => 'Resources', + 'kernel.cache_dir' => '/home/antoine/htdocs/symfony/vendor/bundles/Knplabs/Bundle/GaufretteBundle/Tests/Resources/cache/test', + 'kernel.logs_dir' => '/home/antoine/htdocs/symfony/vendor/bundles/Knplabs/Bundle/GaufretteBundle/Tests/Resources/logs', + 'kernel.bundles' => array( + 'KnplabsGaufretteBundle' => 'Knplabs\\Bundle\\GaufretteBundle\\KnplabsGaufretteBundle', + ), + 'kernel.charset' => 'UTF-8', + 'kernel.container_class' => 'ResourcesTestProjectContainer', + 'knplabs_gaufrette.filesystem.class' => 'Gaufrette\\Filesystem\\Filesystem', + 'knplabs_gaufrette.adapter.in_memory.class' => 'Gaufrette\\Filesystem\\Adapter\\InMemory', + 'knplabs_gaufrette.adapter.local.class' => 'Gaufrette\\Filesystem\\Adapter\\Local', + 'knplabs_gaufrette.adapter.safe_local.class' => 'Gaufrette\\Filesystem\\Adapter\\SafeLocal', + 'kernel.compiled_classes' => array( + ), + ); + } +} diff --git a/Tests/Resources/config/config.yml b/Tests/Resources/config/config.yml new file mode 100644 index 0000000..f884384 --- /dev/null +++ b/Tests/Resources/config/config.yml @@ -0,0 +1,11 @@ +knplabs_gaufrette: + adapters: + foo: + local: + directory: %kernel.root_dir% + create: true + + filesystems: + foo: + adapter: foo + alias: foo_filesystem diff --git a/Tests/Resources/config/config_dev.yml b/Tests/Resources/config/config_dev.yml new file mode 100644 index 0000000..8e0b6ff --- /dev/null +++ b/Tests/Resources/config/config_dev.yml @@ -0,0 +1,2 @@ +imports: + - { resource: config.yml } diff --git a/Tests/Resources/config/config_test.yml b/Tests/Resources/config/config_test.yml new file mode 100644 index 0000000..02bacfd --- /dev/null +++ b/Tests/Resources/config/config_test.yml @@ -0,0 +1,7 @@ +imports: + - { resource: config.yml } + +knplabs_gaufrette: + adapters: + foo: + in_memory: ~ diff --git a/Tests/TestKernel.php b/Tests/TestKernel.php new file mode 100644 index 0000000..d72025f --- /dev/null +++ b/Tests/TestKernel.php @@ -0,0 +1,26 @@ +load(__DIR__.'/Resources/config/config_'.$this->getEnvironment().'.yml'); + } +}