From 4bea06f014e67231d980642fd5e0a9ff7f977a55 Mon Sep 17 00:00:00 2001 From: Rob Frawley 2nd Date: Fri, 13 Jan 2017 17:05:28 -0500 Subject: [PATCH] allow multiple root paths for filesystem loader --- Binary/Loader/FileSystemLoader.php | 70 ++++++++++++----- .../Loader/FileSystemLoaderFactory.php | 14 ++-- Tests/Binary/Loader/FileSystemLoaderTest.php | 75 +++++++++++++++---- .../Loader/FileSystemLoaderFactoryTest.php | 24 +++++- 4 files changed, 144 insertions(+), 39 deletions(-) diff --git a/Binary/Loader/FileSystemLoader.php b/Binary/Loader/FileSystemLoader.php index 512c7b4d3..02c7499b1 100644 --- a/Binary/Loader/FileSystemLoader.php +++ b/Binary/Loader/FileSystemLoader.php @@ -32,26 +32,31 @@ class FileSystemLoader implements LoaderInterface /** * @var string */ - protected $rootPath; + protected $dataRoots; /** * @param MimeTypeGuesserInterface $mimeTypeGuesser * @param ExtensionGuesserInterface $extensionGuesser - * @param string $rootPath + * @param string[] $dataRoots */ public function __construct( MimeTypeGuesserInterface $mimeTypeGuesser, ExtensionGuesserInterface $extensionGuesser, - $rootPath + $dataRoots ) { $this->mimeTypeGuesser = $mimeTypeGuesser; $this->extensionGuesser = $extensionGuesser; + $this->dataRoots = array_map(function ($root) { + if (empty($root) || !($root = realpath($root))) { + throw new InvalidArgumentException(sprintf('Root image path not resolvable "%s"', $root)); + } - if (empty($rootPath) || !($realRootPath = realpath($rootPath))) { - throw new InvalidArgumentException(sprintf('Root image path not resolvable "%s"', $rootPath)); - } + return $root; + }, (array) $dataRoots); - $this->rootPath = $realRootPath; + if (count($this->dataRoots) === 0) { + throw new InvalidArgumentException('One or more data root paths must be specified.'); + } } /** @@ -59,20 +64,49 @@ public function __construct( */ public function find($path) { - if (!($absolutePath = realpath($this->rootPath.DIRECTORY_SEPARATOR.$path))) { - throw new NotLoadableException(sprintf('Source image not resolvable "%s"', $path)); - } + $path = $this->absolutePathRestrict($this->absolutePathLocate($path)); + $mime = $this->mimeTypeGuesser->guess($path); - if (0 !== strpos($absolutePath, $this->rootPath)) { - throw new NotLoadableException(sprintf('Source image invalid "%s" as it is outside of the defined root path', $absolutePath)); + return new FileBinary($path, $mime, $this->extensionGuesser->guess($mime)); + } + + /** + * @param string $path + * + * @return string + */ + private function absolutePathLocate($path) + { + foreach ($this->dataRoots as $root) { + if ($absolutePath = realpath($root.DIRECTORY_SEPARATOR.$path)) { + return $absolutePath; + } } - $mimeType = $this->mimeTypeGuesser->guess($absolutePath); + throw new NotLoadableException(sprintf( + 'Source image not resolvable "%s" in root path(s) "%s"', + $path, + implode(':', $this->dataRoots) + )); + } + + /** + * @param string $path + * + * @return mixed + */ + private function absolutePathRestrict($path) + { + foreach ($this->dataRoots as $root) { + if (0 === strpos($path, $root)) { + return $path; + } + } - return new FileBinary( - $absolutePath, - $mimeType, - $this->extensionGuesser->guess($mimeType) - ); + throw new NotLoadableException(sprintf( + 'Source image invalid "%s" as it is outside of the defined root paths "%s"', + $path, + implode(':', $this->dataRoots) + )); } } diff --git a/DependencyInjection/Factory/Loader/FileSystemLoaderFactory.php b/DependencyInjection/Factory/Loader/FileSystemLoaderFactory.php index 9b41ec514..5405b0122 100644 --- a/DependencyInjection/Factory/Loader/FileSystemLoaderFactory.php +++ b/DependencyInjection/Factory/Loader/FileSystemLoaderFactory.php @@ -47,10 +47,14 @@ public function getName() */ public function addConfiguration(ArrayNodeDefinition $builder) { - $builder - ->children() - ->scalarNode('data_root')->defaultValue('%kernel.root_dir%/../web')->cannotBeEmpty()->end() - ->end() - ; + $builder->children() + ->arrayNode('data_root') + ->beforeNormalization() + ->ifString() + ->then(function ($value) { return array($value); }) + ->end() + ->defaultValue(array('%kernel.root_dir%/../web')) + ->prototype('scalar')->end() + ->end(); } } diff --git a/Tests/Binary/Loader/FileSystemLoaderTest.php b/Tests/Binary/Loader/FileSystemLoaderTest.php index ad60b7da8..be9279df6 100644 --- a/Tests/Binary/Loader/FileSystemLoaderTest.php +++ b/Tests/Binary/Loader/FileSystemLoaderTest.php @@ -20,20 +20,6 @@ */ class FileSystemLoaderTest extends \PHPUnit_Framework_TestCase { - public static function provideLoadCases() - { - $fileName = pathinfo(__FILE__, PATHINFO_BASENAME); - - return array( - array(__DIR__, $fileName), - array(__DIR__.'/', $fileName), - array(__DIR__, '/'.$fileName), - array(__DIR__.'/../../Binary/Loader', '/'.$fileName), - array(realpath(__DIR__.'/..'), 'Loader/'.$fileName), - array(__DIR__.'/../', '/Loader/../../Binary/Loader/'.$fileName), - ); - } - public function testShouldImplementLoaderInterface() { $rc = new \ReflectionClass('Liip\ImagineBundle\Binary\Loader\FileSystemLoader'); @@ -50,6 +36,20 @@ public function testCouldBeConstructedWithExpectedArguments() ); } + public function testThrowExceptionIfNoRootPathsProvided() + { + $this->setExpectedException( + 'Liip\ImagineBundle\Exception\InvalidArgumentException', + 'One or more data root paths must be specified.' + ); + + new FileSystemLoader( + MimeTypeGuesser::getInstance(), + ExtensionGuesser::getInstance(), + array() + ); + } + public function testThrowExceptionIfRootPathIsEmpty() { $this->setExpectedException( @@ -137,6 +137,20 @@ public function testThrowExceptionIfFileNotExist() $loader->find('fileNotExist'); } + public static function provideLoadCases() + { + $fileName = pathinfo(__FILE__, PATHINFO_BASENAME); + + return array( + array(__DIR__, $fileName), + array(__DIR__.'/', $fileName), + array(__DIR__, '/'.$fileName), + array(__DIR__.'/../../Binary/Loader', '/'.$fileName), + array(realpath(__DIR__.'/..'), 'Loader/'.$fileName), + array(__DIR__.'/../', '/Loader/../../Binary/Loader/'.$fileName), + ); + } + /** * @dataProvider provideLoadCases */ @@ -153,4 +167,37 @@ public function testLoad($rootDir, $path) $this->assertInstanceOf('Liip\ImagineBundle\Model\FileBinary', $binary); $this->assertStringStartsWith('text/', $binary->getMimeType()); } + + public static function provideMultipleRootLoadCases() + { + $prepend = array( + realpath(__DIR__.'/../'), + realpath(__DIR__.'/../../'), + realpath(__DIR__.'/../../../'), + ); + + return array_map(function ($params) use ($prepend) { + return array( + array($prepend[mt_rand(0, count($prepend) - 1)], $params[0]), + $params[1] + ); + }, static::provideLoadCases()); + } + + /** + * @dataProvider provideMultipleRootLoadCases + */ + public function testMultipleRootLoadCases($rootDirs, $path) + { + $loader = new FileSystemLoader( + MimeTypeGuesser::getInstance(), + ExtensionGuesser::getInstance(), + $rootDirs + ); + + $binary = $loader->find($path); + + $this->assertInstanceOf('Liip\ImagineBundle\Model\FileBinary', $binary); + $this->assertStringStartsWith('text/', $binary->getMimeType()); + } } diff --git a/Tests/DependencyInjection/Factory/Loader/FileSystemLoaderFactoryTest.php b/Tests/DependencyInjection/Factory/Loader/FileSystemLoaderFactoryTest.php index ea90a36d5..335dff92b 100644 --- a/Tests/DependencyInjection/Factory/Loader/FileSystemLoaderFactoryTest.php +++ b/Tests/DependencyInjection/Factory/Loader/FileSystemLoaderFactoryTest.php @@ -61,7 +61,7 @@ public function testCreateLoaderDefinitionOnCreate() public function testProcessCorrectlyOptionsOnAddConfiguration() { - $expectedDataRoot = 'theDataRoot'; + $expectedDataRoot = array('theDataRoot'); $treeBuilder = new TreeBuilder(); $rootNode = $treeBuilder->root('filesystem', 'array'); @@ -81,7 +81,7 @@ public function testProcessCorrectlyOptionsOnAddConfiguration() public function testAddDefaultOptionsIfNotSetOnAddConfiguration() { - $expectedDataRoot = '%kernel.root_dir%/../web'; + $expectedDataRoot = array('%kernel.root_dir%/../web'); $treeBuilder = new TreeBuilder(); $rootNode = $treeBuilder->root('filesystem', 'array'); @@ -97,6 +97,26 @@ public function testAddDefaultOptionsIfNotSetOnAddConfiguration() $this->assertEquals($expectedDataRoot, $config['data_root']); } + public function testAddAsScalarExpectingArrayNormalizationOfConfiguration() + { + $expectedDataRoot = array('%kernel.root_dir%/../web'); + + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('filesystem', 'array'); + + $loader = new FileSystemLoaderFactory(); + $loader->addConfiguration($rootNode); + + $config = $this->processConfigTree($treeBuilder, array( + 'filesystem' => array( + 'data_root' => $expectedDataRoot[0], + ), + )); + + $this->assertArrayHasKey('data_root', $config); + $this->assertEquals($expectedDataRoot, $config['data_root']); + } + /** * @param TreeBuilder $treeBuilder * @param array $configs