-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
FileCollectionInterface
and AbstractFileCollection
classes
- Loading branch information
Showing
3 changed files
with
267 additions
and
198 deletions.
There are no files selected for viewing
227 changes: 227 additions & 0 deletions
227
system/src/Grav/Framework/Collection/AbstractFileCollection.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
<?php | ||
/** | ||
* @package Grav\Framework\Collection | ||
* | ||
* @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. | ||
* @license MIT License; see LICENSE file for details. | ||
*/ | ||
|
||
namespace Grav\Framework\Collection; | ||
|
||
use Doctrine\Common\Collections\Criteria; | ||
use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor; | ||
use Grav\Common\Grav; | ||
use RocketTheme\Toolbox\ResourceLocator\RecursiveUniformResourceIterator; | ||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; | ||
|
||
/** | ||
* Collection of objects stored into a filesystem. | ||
* | ||
* @package Grav\Framework\Collection | ||
*/ | ||
class AbstractFileCollection extends AbstractLazyCollection implements FileCollectionInterface | ||
{ | ||
/** | ||
* @var string | ||
*/ | ||
protected $path; | ||
|
||
/** | ||
* @var \RecursiveDirectoryIterator|RecursiveUniformResourceIterator | ||
*/ | ||
protected $iterator; | ||
|
||
/** | ||
* @var callable | ||
*/ | ||
protected $createObjectFunction; | ||
|
||
/** | ||
* @var callable | ||
*/ | ||
protected $filterFunction; | ||
|
||
/** | ||
* @var int | ||
*/ | ||
protected $flags; | ||
|
||
/** | ||
* @var int | ||
*/ | ||
protected $nestingLimit; | ||
|
||
/** | ||
* @param string $path | ||
*/ | ||
protected function __construct($path) | ||
{ | ||
$this->path = $path; | ||
$this->flags = self::INCLUDE_FILES | self::INCLUDE_FOLDERS; | ||
$this->nestingLimit = 0; | ||
$this->createObjectFunction = [$this, 'createObject']; | ||
|
||
$this->setIterator(); | ||
} | ||
|
||
/** | ||
* @return string | ||
*/ | ||
public function getPath() | ||
{ | ||
return $this->path; | ||
} | ||
|
||
/** | ||
* @param Criteria $criteria | ||
* @return ArrayCollection | ||
* @todo Implement lazy matching | ||
*/ | ||
public function matching(Criteria $criteria) | ||
{ | ||
$expr = $criteria->getWhereExpression(); | ||
|
||
$oldFilter = $this->filterFunction; | ||
if ($expr) { | ||
$visitor = new ClosureExpressionVisitor(); | ||
$filter = $visitor->dispatch($expr); | ||
$this->addFilter($filter); | ||
} | ||
|
||
$filtered = $this->doInitializeByIterator($this->iterator, $this->nestingLimit); | ||
$this->filterFunction = $oldFilter; | ||
|
||
if ($orderings = $criteria->getOrderings()) { | ||
$next = null; | ||
foreach (array_reverse($orderings) as $field => $ordering) { | ||
$next = ClosureExpressionVisitor::sortByField($field, $ordering == Criteria::DESC ? -1 : 1, $next); | ||
} | ||
|
||
uasort($filtered, $next); | ||
} else { | ||
ksort($filtered); | ||
} | ||
|
||
$offset = $criteria->getFirstResult(); | ||
$length = $criteria->getMaxResults(); | ||
|
||
if ($offset || $length) { | ||
$filtered = array_slice($filtered, (int)$offset, $length); | ||
} | ||
|
||
return new ArrayCollection($filtered); | ||
} | ||
|
||
protected function setIterator() | ||
{ | ||
$iteratorFlags = \RecursiveDirectoryIterator::SKIP_DOTS + \FilesystemIterator::UNIX_PATHS | ||
+ \FilesystemIterator::CURRENT_AS_SELF + \FilesystemIterator::FOLLOW_SYMLINKS; | ||
|
||
if (strpos($this->path, '://')) { | ||
/** @var UniformResourceLocator $locator */ | ||
$locator = Grav::instance()['locator']; | ||
$this->iterator = $locator->getRecursiveIterator($this->path, $iteratorFlags); | ||
} else { | ||
$this->iterator = new \RecursiveDirectoryIterator($this->path, $iteratorFlags); | ||
} | ||
} | ||
|
||
/** | ||
* @param callable $filterFunction | ||
* @return $this | ||
*/ | ||
protected function addFilter(callable $filterFunction) | ||
{ | ||
if ($this->filterFunction) { | ||
$oldFilterFunction = $this->filterFunction; | ||
$this->filterFunction = function ($expr) use ($oldFilterFunction, $filterFunction) { | ||
return $oldFilterFunction($expr) && $filterFunction($expr); | ||
}; | ||
} else { | ||
$this->filterFunction = $filterFunction; | ||
} | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
protected function doInitialize() | ||
{ | ||
$filtered = $this->doInitializeByIterator($this->iterator, $this->nestingLimit); | ||
ksort($filtered); | ||
|
||
$this->collection = new ArrayCollection($filtered); | ||
} | ||
|
||
protected function doInitializeByIterator(\SeekableIterator $iterator, $nestingLimit) | ||
{ | ||
$children = []; | ||
$objects = []; | ||
$filter = $this->filterFunction; | ||
$objectFunction = $this->createObjectFunction; | ||
|
||
/** @var \RecursiveDirectoryIterator $file */ | ||
foreach ($iterator as $file) { | ||
// Skip files if they shouldn't be included. | ||
if (!($this->flags & static::INCLUDE_FILES) && $file->isFile()) { | ||
continue; | ||
} | ||
|
||
// Apply main filter. | ||
if ($filter && !$filter($file)) { | ||
continue; | ||
} | ||
|
||
// Include children if the recursive flag is set. | ||
if (($this->flags & static::RECURSIVE) && $nestingLimit > 0 && $file->hasChildren()) { | ||
$children[] = $file->getChildren(); | ||
} | ||
|
||
// Skip folders if they shouldn't be included. | ||
if (!($this->flags & static::INCLUDE_FOLDERS) && $file->isDir()) { | ||
continue; | ||
} | ||
|
||
$object = $objectFunction($file); | ||
$objects[$object->key] = $object; | ||
} | ||
|
||
if ($children) { | ||
$objects += $this->doInitializeChildren($children, $nestingLimit - 1); | ||
} | ||
|
||
return $objects; | ||
} | ||
|
||
/** | ||
* @param \RecursiveDirectoryIterator[] $children | ||
* @return array | ||
*/ | ||
protected function doInitializeChildren(array $children, $nestingLimit) | ||
{ | ||
$objects = []; | ||
|
||
foreach ($children as $iterator) { | ||
$objects += $this->doInitializeByIterator($iterator, $nestingLimit); | ||
} | ||
|
||
return $objects; | ||
} | ||
|
||
/** | ||
* @param \RecursiveDirectoryIterator $file | ||
* @return object | ||
*/ | ||
protected function createObject($file) | ||
{ | ||
return (object) [ | ||
'key' => $file->getSubPathname(), | ||
'type' => $file->isDir() ? 'folder' : 'file:' . $file->getExtension(), | ||
'url' => method_exists($file, 'getUrl') ? $file->getUrl() : null, | ||
'pathname' => $file->getPathname(), | ||
'mtime' => $file->getMTime() | ||
]; | ||
} | ||
} |
Oops, something went wrong.