updated core to 1.7.15
This commit is contained in:
332
system/src/Grav/Framework/Flex/Flex.php
Normal file
332
system/src/Grav/Framework/Flex/Flex.php
Normal file
@@ -0,0 +1,332 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @package Grav\Framework\Flex
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Framework\Flex;
|
||||
|
||||
use Grav\Common\Debugger;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Framework\Flex\Interfaces\FlexCollectionInterface;
|
||||
use Grav\Framework\Flex\Interfaces\FlexInterface;
|
||||
use Grav\Framework\Flex\Interfaces\FlexObjectInterface;
|
||||
use Grav\Framework\Object\ObjectCollection;
|
||||
use RuntimeException;
|
||||
use function count;
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
* Class Flex
|
||||
* @package Grav\Framework\Flex
|
||||
*/
|
||||
class Flex implements FlexInterface
|
||||
{
|
||||
/** @var array */
|
||||
protected $config;
|
||||
/** @var FlexDirectory[] */
|
||||
protected $types;
|
||||
|
||||
/**
|
||||
* Flex constructor.
|
||||
* @param array $types List of [type => blueprint file, ...]
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct(array $types, array $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->types = [];
|
||||
|
||||
foreach ($types as $type => $blueprint) {
|
||||
if (!file_exists($blueprint)) {
|
||||
/** @var Debugger $debugger */
|
||||
$debugger = Grav::instance()['debugger'];
|
||||
$debugger->addMessage(sprintf('Flex: blueprint for flex type %s is missing', $type), 'error');
|
||||
|
||||
continue;
|
||||
}
|
||||
$this->addDirectoryType($type, $blueprint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param string $blueprint
|
||||
* @param array $config
|
||||
* @return $this
|
||||
*/
|
||||
public function addDirectoryType(string $type, string $blueprint, array $config = [])
|
||||
{
|
||||
$config = array_replace_recursive(['enabled' => true], $this->config ?? [], $config);
|
||||
|
||||
$this->types[$type] = new FlexDirectory($type, $blueprint, $config);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FlexDirectory $directory
|
||||
* @return $this
|
||||
*/
|
||||
public function addDirectory(FlexDirectory $directory)
|
||||
{
|
||||
$this->types[$directory->getFlexType()] = $directory;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @return bool
|
||||
*/
|
||||
public function hasDirectory(string $type): bool
|
||||
{
|
||||
return isset($this->types[$type]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|string[]|null $types
|
||||
* @param bool $keepMissing
|
||||
* @return array<FlexDirectory|null>
|
||||
*/
|
||||
public function getDirectories(array $types = null, bool $keepMissing = false): array
|
||||
{
|
||||
if ($types === null) {
|
||||
return $this->types;
|
||||
}
|
||||
|
||||
// Return the directories in the given order.
|
||||
$directories = [];
|
||||
foreach ($types as $type) {
|
||||
$directories[$type] = $this->types[$type] ?? null;
|
||||
}
|
||||
|
||||
return $keepMissing ? $directories : array_filter($directories);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @return FlexDirectory|null
|
||||
*/
|
||||
public function getDirectory(string $type): ?FlexDirectory
|
||||
{
|
||||
return $this->types[$type] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param array|null $keys
|
||||
* @param string|null $keyField
|
||||
* @return FlexCollectionInterface|null
|
||||
*/
|
||||
public function getCollection(string $type, array $keys = null, string $keyField = null): ?FlexCollectionInterface
|
||||
{
|
||||
$directory = $type ? $this->getDirectory($type) : null;
|
||||
|
||||
return $directory ? $directory->getCollection($keys, $keyField) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $keys
|
||||
* @param array $options In addition to the options in getObjects(), following options can be passed:
|
||||
* collection_class: Class to be used to create the collection. Defaults to ObjectCollection.
|
||||
* @return FlexCollectionInterface
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function getMixedCollection(array $keys, array $options = []): FlexCollectionInterface
|
||||
{
|
||||
$collectionClass = $options['collection_class'] ?? ObjectCollection::class;
|
||||
if (!class_exists($collectionClass)) {
|
||||
throw new RuntimeException(sprintf('Cannot create collection: Class %s does not exist', $collectionClass));
|
||||
}
|
||||
|
||||
$objects = $this->getObjects($keys, $options);
|
||||
|
||||
return new $collectionClass($objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $keys
|
||||
* @param array $options Following optional options can be passed:
|
||||
* types: List of allowed types.
|
||||
* type: Allowed type if types isn't defined, otherwise acts as default_type.
|
||||
* default_type: Set default type for objects given without type (only used if key_field isn't set).
|
||||
* keep_missing: Set to true if you want to return missing objects as null.
|
||||
* key_field: Key field which is used to match the objects.
|
||||
* @return array
|
||||
*/
|
||||
public function getObjects(array $keys, array $options = []): array
|
||||
{
|
||||
$type = $options['type'] ?? null;
|
||||
$defaultType = $options['default_type'] ?? $type ?? null;
|
||||
$keyField = $options['key_field'] ?? 'flex_key';
|
||||
|
||||
// Prepare empty result lists for all requested Flex types.
|
||||
$types = $options['types'] ?? (array)$type ?: null;
|
||||
if ($types) {
|
||||
$types = array_fill_keys($types, []);
|
||||
}
|
||||
$strict = isset($types);
|
||||
|
||||
$guessed = [];
|
||||
if ($keyField === 'flex_key') {
|
||||
// We need to split Flex key lookups into individual directories.
|
||||
$undefined = [];
|
||||
$keyFieldFind = 'storage_key';
|
||||
|
||||
foreach ($keys as $flexKey) {
|
||||
if (!$flexKey) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$flexKey = (string)$flexKey;
|
||||
// Normalize key and type using fallback to default type if it was set.
|
||||
[$key, $type, $guess] = $this->resolveKeyAndType($flexKey, $defaultType);
|
||||
|
||||
if ($type === '' && $types) {
|
||||
// Add keys which are not associated to any Flex type. They will be included to every Flex type.
|
||||
foreach ($types as $type => &$array) {
|
||||
$array[] = $key;
|
||||
$guessed[$key][] = "{$type}.obj:{$key}";
|
||||
}
|
||||
unset($array);
|
||||
} elseif (!$strict || isset($types[$type])) {
|
||||
// Collect keys by their Flex type. If allowed types are defined, only include values from those types.
|
||||
$types[$type][] = $key;
|
||||
if ($guess) {
|
||||
$guessed[$key][] = "{$type}.obj:{$key}";
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We are using a specific key field, make every key undefined.
|
||||
$undefined = $keys;
|
||||
$keyFieldFind = $keyField;
|
||||
}
|
||||
|
||||
if (!$types) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$list = [[]];
|
||||
foreach ($types as $type => $typeKeys) {
|
||||
// Also remember to look up keys from undefined Flex types.
|
||||
$lookupKeys = $undefined ? array_merge($typeKeys, $undefined) : $typeKeys;
|
||||
|
||||
$collection = $this->getCollection($type, $lookupKeys, $keyFieldFind);
|
||||
if ($collection && $keyFieldFind !== $keyField) {
|
||||
$collection = $collection->withKeyField($keyField);
|
||||
}
|
||||
|
||||
$list[] = $collection ? $collection->toArray() : [];
|
||||
}
|
||||
|
||||
// Merge objects from individual types back together.
|
||||
$list = array_merge(...$list);
|
||||
|
||||
// Use the original key ordering.
|
||||
if (!$guessed) {
|
||||
$list = array_replace(array_fill_keys($keys, null), $list) ?? [];
|
||||
} else {
|
||||
// We have mixed keys, we need to map flex keys back to storage keys.
|
||||
$results = [];
|
||||
foreach ($keys as $key) {
|
||||
$flexKey = $guessed[$key] ?? $key;
|
||||
if (is_array($flexKey)) {
|
||||
$result = null;
|
||||
foreach ($flexKey as $tryKey) {
|
||||
if ($result = $list[$tryKey] ?? null) {
|
||||
// Use the first matching object (conflicting objects will be ignored for now).
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$result = $list[$flexKey] ?? null;
|
||||
}
|
||||
|
||||
$results[$key] = $result;
|
||||
}
|
||||
|
||||
$list = $results;
|
||||
}
|
||||
|
||||
// Remove missing objects if not asked to keep them.
|
||||
if (empty($option['keep_missing'])) {
|
||||
$list = array_filter($list);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param string|null $type
|
||||
* @param string|null $keyField
|
||||
* @return FlexObjectInterface|null
|
||||
*/
|
||||
public function getObject(string $key, string $type = null, string $keyField = null): ?FlexObjectInterface
|
||||
{
|
||||
if (null === $type && null === $keyField) {
|
||||
// Special handling for quick Flex key lookups.
|
||||
$keyField = 'storage_key';
|
||||
[$key, $type] = $this->resolveKeyAndType($key, $type);
|
||||
} else {
|
||||
$type = $this->resolveType($type);
|
||||
}
|
||||
|
||||
if ($type === '' || $key === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$directory = $this->getDirectory($type);
|
||||
|
||||
return $directory ? $directory->getObject($key, $keyField) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->types);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $flexKey
|
||||
* @param string|null $type
|
||||
* @return array
|
||||
*/
|
||||
protected function resolveKeyAndType(string $flexKey, string $type = null): array
|
||||
{
|
||||
$guess = false;
|
||||
if (strpos($flexKey, ':') !== false) {
|
||||
[$type, $key] = explode(':', $flexKey, 2);
|
||||
|
||||
$type = $this->resolveType($type);
|
||||
} else {
|
||||
$key = $flexKey;
|
||||
$type = (string)$type;
|
||||
$guess = true;
|
||||
}
|
||||
|
||||
return [$key, $type, $guess];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $type
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveType(string $type = null): string
|
||||
{
|
||||
if (null !== $type && strpos($type, '.') !== false) {
|
||||
return preg_replace('|\.obj$|', '', $type) ?? $type;
|
||||
}
|
||||
|
||||
return $type ?? '';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user