first commit

This commit is contained in:
2019-03-28 17:57:56 +01:00
commit b0e25fd66f
561 changed files with 56803 additions and 0 deletions

View File

@@ -0,0 +1,227 @@
<?php
/**
* @package Grav\Framework\Collection
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, 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()
];
}
}

View File

@@ -0,0 +1,62 @@
<?php
/**
* @package Grav\Framework\Collection
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Framework\Collection;
use Doctrine\Common\Collections\AbstractLazyCollection as BaseAbstractLazyCollection;
/**
* General JSON serializable collection.
*
* @package Grav\Framework\Collection
*/
abstract class AbstractLazyCollection extends BaseAbstractLazyCollection implements CollectionInterface
{
/**
* The backed collection to use
*
* @var ArrayCollection
*/
protected $collection;
/**
* {@inheritDoc}
*/
public function reverse()
{
$this->initialize();
return $this->collection->reverse();
}
/**
* {@inheritDoc}
*/
public function shuffle()
{
$this->initialize();
return $this->collection->shuffle();
}
/**
* {@inheritDoc}
*/
public function chunk($size)
{
$this->initialize();
return $this->collection->chunk($size);
}
/**
* {@inheritDoc}
*/
public function jsonSerialize()
{
$this->initialize();
return $this->collection->jsonSerialize();
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* @package Grav\Framework\Collection
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Framework\Collection;
use Doctrine\Common\Collections\ArrayCollection as BaseArrayCollection;
/**
* General JSON serializable collection.
*
* @package Grav\Framework\Collection
*/
class ArrayCollection extends BaseArrayCollection implements CollectionInterface
{
/**
* Reverse the order of the items.
*
* @return static
*/
public function reverse()
{
return $this->createFrom(array_reverse($this->toArray()));
}
/**
* Shuffle items.
*
* @return static
*/
public function shuffle()
{
$keys = $this->getKeys();
shuffle($keys);
return $this->createFrom(array_replace(array_flip($keys), $this->toArray()));
}
/**
* Split collection into chunks.
*
* @param int $size Size of each chunk.
* @return array
*/
public function chunk($size)
{
return array_chunk($this->toArray(), $size, true);
}
/**
* Implementes JsonSerializable interface.
*
* @return array
*/
public function jsonSerialize()
{
return $this->toArray();
}
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* @package Grav\Framework\Collection
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Framework\Collection;
use Doctrine\Common\Collections\Collection;
/**
* Collection Interface.
*
* @package Grav\Framework\Collection
*/
interface CollectionInterface extends Collection, \JsonSerializable
{
/**
* Reverse the order of the items.
*
* @return static
*/
public function reverse();
/**
* Shuffle items.
*
* @return static
*/
public function shuffle();
/**
* Split collection into chunks.
*
* @param int $size Size of each chunk.
* @return array
*/
public function chunk($size);
}

View File

@@ -0,0 +1,93 @@
<?php
/**
* @package Grav\Framework\Collection
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Framework\Collection;
/**
* Collection of objects stored into a filesystem.
*
* @package Grav\Framework\Collection
*/
class FileCollection extends AbstractFileCollection
{
/**
* @param string $path
* @param int $flags
*/
public function __construct($path, $flags = null)
{
parent::__construct($path);
$this->flags = (int)($flags ?: self::INCLUDE_FILES | self::INCLUDE_FOLDERS | self::RECURSIVE);
$this->setIterator();
$this->setFilter();
$this->setObjectBuilder();
$this->setNestingLimit();
}
/**
* @return int
*/
public function getFlags()
{
return $this->flags;
}
/**
* @return int
*/
public function getNestingLimit()
{
return $this->nestingLimit;
}
/**
* @param int $limit
* @return $this
*/
public function setNestingLimit($limit = 99)
{
$this->nestingLimit = (int) $limit;
return $this;
}
/**
* @param callable|null $filterFunction
* @return $this
*/
public function setFilter(callable $filterFunction = null)
{
$this->filterFunction = $filterFunction;
return $this;
}
/**
* @param callable $filterFunction
* @return $this
*/
public function addFilter(callable $filterFunction)
{
parent::addFilter($filterFunction);
return $this;
}
/**
* @param callable|null $objectFunction
* @return $this
*/
public function setObjectBuilder(callable $objectFunction = null)
{
$this->createObjectFunction = $objectFunction ?: [$this, 'createObject'];
return $this;
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* @package Grav\Framework\Collection
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Framework\Collection;
use Doctrine\Common\Collections\Selectable;
/**
* Collection of objects stored into a filesystem.
*
* @package Grav\Framework\Collection
*/
interface FileCollectionInterface extends CollectionInterface, Selectable
{
const INCLUDE_FILES = 1;
const INCLUDE_FOLDERS = 2;
const RECURSIVE = 4;
/**
* @return string
*/
public function getPath();
}