Skip to content

Commit

Permalink
allow multiple root paths for filesystem loader
Browse files Browse the repository at this point in the history
  • Loading branch information
robfrawley committed Jan 13, 2017
1 parent 966f419 commit c836fda
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 38 deletions.
78 changes: 61 additions & 17 deletions Binary/Loader/FileSystemLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,47 +32,91 @@ 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 ($dataRoot) {
return $this->dataRootReal($dataRoot);
}, (array) $dataRoots);

if (empty($rootPath) || !($realRootPath = realpath($rootPath))) {
throw new InvalidArgumentException(sprintf('Root image path not resolvable "%s"', $rootPath));
if (count($this->dataRoots) === 0) {
throw new InvalidArgumentException('One or more data root paths must be specified.');
}

$this->rootPath = $realRootPath;
}

/**
* {@inheritdoc}
*/
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);

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;
}
}

if (0 !== strpos($absolutePath, $this->rootPath)) {
throw new NotLoadableException(sprintf('Source image invalid "%s" as it is outside of the defined root path', $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;
}
}

$mimeType = $this->mimeTypeGuesser->guess($absolutePath);
throw new NotLoadableException(sprintf(
'Source image invalid "%s" as it is outside of the defined root paths "%s"',
$path,
implode(':', $this->dataRoots)
));
}

/**
* @param string $root
*
* @return string
*/
private function dataRootReal($root)
{
if (empty($root) || !($root = realpath($root))) {
throw new InvalidArgumentException(sprintf('Root image path not resolvable "%s"', $root));
}

return new FileBinary(
$absolutePath,
$mimeType,
$this->extensionGuesser->guess($mimeType)
);
return $root;
}
}
16 changes: 11 additions & 5 deletions DependencyInjection/Factory/Loader/FileSystemLoaderFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,16 @@ 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(['%kernel.root_dir%/../web'])
->prototype('scalar')->end()
->end();
}
}
73 changes: 59 additions & 14 deletions Tests/Binary/Loader/FileSystemLoaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -110,6 +96,20 @@ public function testThrowExceptionIfRealPathIsOutsideRootPath2()
$loader->find('../../Binary/');
}

public function testThrowExceptionIfNoRootPathsProvided()
{
$this->setExpectedException(
'Liip\ImagineBundle\Exception\InvalidArgumentException',
'One or more data root paths must be specified.'
);

new FileSystemLoader(
MimeTypeGuesser::getInstance(),
ExtensionGuesser::getInstance(),
[]
);
}

public function testThrowExceptionIfPathHasDoublePointSlashInTheMiddle()
{
$loader = new FileSystemLoader(
Expand Down Expand Up @@ -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
*/
Expand All @@ -153,4 +167,35 @@ public function testLoad($rootDir, $path)
$this->assertInstanceOf('Liip\ImagineBundle\Model\FileBinary', $binary);
$this->assertStringStartsWith('text/', $binary->getMimeType());
}

public static function provideMultipleRootLoadCases()
{
$fileName = pathinfo(__FILE__, PATHINFO_BASENAME);

return array(
array([realpath(__DIR__.'/../../'), __DIR__], $fileName),
array([realpath(__DIR__.'/../'), __DIR__.'/'], $fileName),
array([realpath(__DIR__.'/../../../'), __DIR__], '/'.$fileName),
array([realpath(__DIR__.'/../'), __DIR__.'/../../Binary/Loader'], '/'.$fileName),
array([realpath(__DIR__.'/../../'), realpath(__DIR__.'/..')], 'Loader/'.$fileName),
array([realpath(__DIR__.'/../../../'), __DIR__.'/../'], '/Loader/../../Binary/Loader/'.$fileName),
);
}

/**
* @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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public function testCreateLoaderDefinitionOnCreate()

public function testProcessCorrectlyOptionsOnAddConfiguration()
{
$expectedDataRoot = 'theDataRoot';
$expectedDataRoot = ['theDataRoot'];

$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('filesystem', 'array');
Expand All @@ -81,7 +81,7 @@ public function testProcessCorrectlyOptionsOnAddConfiguration()

public function testAddDefaultOptionsIfNotSetOnAddConfiguration()
{
$expectedDataRoot = '%kernel.root_dir%/../web';
$expectedDataRoot = ['%kernel.root_dir%/../web'];

$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('filesystem', 'array');
Expand Down

0 comments on commit c836fda

Please sign in to comment.