diff --git a/system/src/Grav/Common/Config/CompiledBlueprints.php b/system/src/Grav/Common/Config/CompiledBlueprints.php index 36fe1b826..daa0bd25d 100644 --- a/system/src/Grav/Common/Config/CompiledBlueprints.php +++ b/system/src/Grav/Common/Config/CompiledBlueprints.php @@ -3,9 +3,7 @@ namespace Grav\Common\Config; use Grav\Common\Data\Blueprint; use Grav\Common\Data\BlueprintForm; -use Grav\Common\File\CompiledYamlFile; use Grav\Common\Grav; -use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; /** * The Compiled Blueprints class. @@ -73,91 +71,10 @@ class CompiledBlueprints extends CompiledBase */ protected function loadFile($name, $files) { - $data = $this->loadBlueprints($files); + // Load blueprint file. + $blueprintForm = new BlueprintForm($files); - // Merge all extends into a single blueprint. - $blueprintForm = new BlueprintForm(array_shift($data)); - foreach ($data as $content) { - $blueprintForm->extend($content, true); - } - $blueprintForm->init(); - - $this->object->embed($name, $blueprintForm->toArray(), '/', true); - } - - /** - * Internal function that handles loading extended blueprints. - * - * @param array $files - * @return array - */ - protected function loadBlueprints(array $files) - { - $filename = array_shift($files); - $file = CompiledYamlFile::instance($filename); - $content = $file->content(); - $file->free(); - - $extends = isset($content['@extends']) ? (array) $content['@extends'] - : (isset($content['extends@']) ? (array) $content['extends@'] : null); - - $data = isset($extends) ? $this->extendBlueprint($files, $extends) : []; - $data[] = $content; - - return $data; - } - - /** - * Internal function to recursively load extended blueprints. - * - * @param array $parents - * @param array $extends - * @return array - */ - protected function extendBlueprint(array $parents, array $extends) - { - if (is_string(key($extends))) { - $extends = [$extends]; - } - - $data = []; - foreach ($extends as $extendConfig) { - // Accept array of type and context or a string. - $extendType = !is_string($extendConfig) - ? !isset($extendConfig['type']) ? null : $extendConfig['type'] : $extendConfig; - - if (!$extendType) { - continue; - } - - if ($extendType === '@parent' || $extendType === 'parent@') { - $files = $parents; - - } else { - if (strpos($extendType, '://')) { - $path = $extendType; - } elseif (empty($extendConfig['context'])) { - $path = "blueprints://{$extendType}"; - } else { - $separator = $extendConfig['context'][strlen($extendConfig['context'])-1] === '/' ? '' : '/'; - $path = $extendConfig['context'] . $separator . $extendType; - } - if (!preg_match('/\.yaml$/', $path)) { - $path .= '.yaml'; - } - - /** @var UniformResourceLocator $locator */ - $locator = Grav::instance()['locator']; - - $files = $locator->findResources($path); - } - - if ($files) { - $data = array_merge($data, $this->loadBlueprints($files)); - } - } - - return $data; + $this->object->embed($name, $blueprintForm->load()->toArray(), '/', true); } /** diff --git a/system/src/Grav/Common/Data/BlueprintForm.php b/system/src/Grav/Common/Data/BlueprintForm.php index 164d12c3b..27bcda8e1 100644 --- a/system/src/Grav/Common/Data/BlueprintForm.php +++ b/system/src/Grav/Common/Data/BlueprintForm.php @@ -2,10 +2,11 @@ namespace Grav\Common\Data; use Grav\Common\File\CompiledYamlFile; -use RocketTheme\Toolbox\ArrayTraits\Constructor; +use Grav\Common\Grav; use RocketTheme\Toolbox\ArrayTraits\Export; use RocketTheme\Toolbox\ArrayTraits\ExportInterface; use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters; +use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; /** * The Config class contains configuration information. @@ -14,15 +15,116 @@ use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters; */ class BlueprintForm implements \ArrayAccess, ExportInterface { - use Constructor, NestedArrayAccessWithGetters, Export; + use NestedArrayAccessWithGetters, Export; /** * @var array */ protected $items; + /** + * @var string + */ + protected $filename; + + /** + * @var string + */ + protected $context = 'blueprints://'; + + /** + * @var array + */ + protected $overrides = []; + + /** + * Constructor. + * + * @param string|array $filename + * @param array $items + */ + public function __construct($filename, array $items = []) + { + $this->filename = $filename; + $this->items = $items; + } + + /** + * Set context for import@ and extend@. + * + * @param $context + * @return $this + */ + public function setContext($context) + { + $this->context = $context; + + return $this; + } + + /** + * Set custom overrides for import@ and extend@. + * + * @param array $overrides + * @return $this + */ + public function setOverrides(array $overrides) + { + $this->overrides = $overrides; + + return $this; + } + + /** + * Load blueprint. + * + * @return $this + */ + public function load() + { + $path = $this->filename; + + if (is_string($path) && !strpos($path, '://')) { + // Resolve filename. + $path = isset($this->overrides[$path]) ? $this->overrides[$path] : "{$this->context}{$path}"; + if (!preg_match('/\.yaml$/', $path)) { + $path .= YAML_EXT; + } + } + + // Only load and extend blueprint if it has not yet been loaded. + if (empty($this->items)) { + // Get list of parent files. + if (is_string($path) && strpos($path, '://')) { + /** @var UniformResourceLocator $locator */ + $locator = Grav::instance()['locator']; + + $files = $locator->findResources($path); + } else { + $files = (array) $path; + } + + // Load and extend blueprints. + $data = $this->doLoad($files); + + $this->items = array_shift($data); + foreach ($data as $content) { + $this->extend($content, true); + } + } + + // Finally initialize blueprint. + return $this->init(); + } + + /** + * Initialize blueprint. + * + * @return $this + */ public function init() { + // Import blueprints. $this->deepInit($this->items); return $this; @@ -209,7 +311,7 @@ class BlueprintForm implements \ArrayAccess, ExportInterface if (strpos($type, '://')) { $filename = $type; } elseif (empty($value['context'])) { - $filename = "blueprints://{$type}"; + $filename = isset($this->overrides[$type]) ? $this->overrides[$type] : "{$this->context}{$type}"; } else { $separator = $value['context'][strlen($value['context'])-1] === '/' ? '' : '/'; $filename = $value['context'] . $separator . $type; @@ -218,15 +320,94 @@ class BlueprintForm implements \ArrayAccess, ExportInterface $filename .= YAML_EXT; } + if (!is_file($filename)) { return; } - $file = CompiledYamlFile::instance($filename); - $blueprint = (new BlueprintForm($file->content()))->init(); + $blueprint = (new BlueprintForm($filename))->setContext($this->context)->setOverrides($this->overrides)->load(); $name = implode('/', $path); $this->embed($name, $blueprint->form(), '/', false); } + + /** + * Internal function that handles loading extended blueprints. + * + * @param array $files + * @return array + */ + protected function doLoad(array $files) + { + $filename = array_shift($files); + $file = CompiledYamlFile::instance($filename); + $content = $file->content(); + $file->free(); + + $extends = isset($content['@extends']) ? (array) $content['@extends'] + : (isset($content['extends@']) ? (array) $content['extends@'] : null); + + $data = isset($extends) ? $this->doExtend($files, $extends) : []; + $data[] = $content; + + return $data; + } + + /** + * Internal function to recursively load extended blueprints. + * + * @param array $parents + * @param array $extends + * @return array + */ + protected function doExtend(array $parents, array $extends) + { + if (is_string(key($extends))) { + $extends = [$extends]; + } + + $data = []; + foreach ($extends as $value) { + // Accept array of type and context or a string. + $type = !is_string($value) + ? !isset($value['type']) ? null : $value['type'] : $value; + + if (!$type) { + continue; + } + + if ($type === '@parent' || $type === 'parent@') { + $files = $parents; + + } else { + if (strpos($type, '://')) { + $path = $type; + } elseif (empty($value['context'])) { + $path = isset($this->overrides[$type]) ? $this->overrides[$type] : "{$this->context}{$type}"; + } else { + $separator = $value['context'][strlen($value['context'])-1] === '/' ? '' : '/'; + $path = $value['context'] . $separator . $type; + } + if (!preg_match('/\.yaml$/', $path)) { + $path .= YAML_EXT; + } + + if (strpos($path, '://')) { + /** @var UniformResourceLocator $locator */ + $locator = Grav::instance()['locator']; + + $files = $locator->findResources($path); + } else { + $files = (array) $path; + } + } + + if ($files) { + $data = array_merge($data, $this->doLoad($files)); + } + } + + return $data; + } } diff --git a/system/src/Grav/Common/Data/Blueprints.php b/system/src/Grav/Common/Data/Blueprints.php index a880cb313..5afa4f93d 100644 --- a/system/src/Grav/Common/Data/Blueprints.php +++ b/system/src/Grav/Common/Data/Blueprints.php @@ -1,7 +1,6 @@ instances[$type])) { - if (!is_string($this->search)) { - $filename = isset($this->search[$type]) ? $this->search[$type] : null; - } else { - $filename = $this->search . $type . YAML_EXT; - } - - // Check if search is a stream and resolve the path. - if ($filename && strpos($filename, '://')) { - /** @var UniformResourceLocator $locator */ - $locator = Grav::instance()['locator']; - - $files = $locator->findResources($filename); - } else { - $files = (array) $filename; - } - - $blueprint = $this->loadFile($type, $files); - - $this->instances[$type] = $blueprint->init(); + $this->instances[$type] = $this->loadFile($type); } return $this->instances[$type]; @@ -90,109 +71,29 @@ class Blueprints $this->types[$name] = ucfirst(strtr($name, '_', ' ')); } } + return $this->types; } /** - * Load single configuration file and make a blueprint. + * Load blueprint file. * - * @param string $name Name of the position. - * @param array $files Files to be loaded. + * @param string $name Name of the blueprint. * @return Blueprint */ - protected function loadFile($name, $files) + protected function loadFile($name) { - $data = $this->loadBlueprints($files); - - // Merge all extends into a single blueprint. - $blueprintForm = new BlueprintForm(array_shift($data)); - foreach ($data as $content) { - $blueprintForm->extend($content, true); + $blueprintForm = new BlueprintForm($name); + if (is_array($this->search)) { + $blueprintForm->setOverrides($this->search); + } else { + $blueprintForm->setContext($this->search); } - $blueprintForm->init(); + $blueprintForm->load(); - $blueprint = new Blueprint($name, $blueprintForm->toArray(), $this); + $blueprint = new Blueprint($name, $blueprintForm->toArray()); - return $blueprint; - } - - /** - * Internal function that handles loading extended blueprints. - * - * @param array $files - * @return array - */ - protected function loadBlueprints(array $files) - { - $filename = array_shift($files); - $file = CompiledYamlFile::instance($filename); - $content = $file->content(); - $file->free(); - - $extends = isset($content['@extends']) ? (array) $content['@extends'] - : (isset($content['extends@']) ? (array) $content['extends@'] : null); - - $data = isset($extends) ? $this->extendBlueprint($files, $extends) : []; - $data[] = $content; - - return $data; - } - - /** - * Internal function to recursively load extended blueprints. - * - * @param array $parents - * @param array $extends - * @return array - */ - protected function extendBlueprint(array $parents, array $extends) - { - if (is_string(key($extends))) { - $extends = [$extends]; - } - - $data = []; - foreach ($extends as $extendConfig) { - // Accept array of type and context or a string. - $extendType = !is_string($extendConfig) - ? !isset($extendConfig['type']) ? null : $extendConfig['type'] : $extendConfig; - - if (!$extendType) { - continue; - } - - if ($extendType === '@parent' || $extendType === 'parent@') { - $files = $parents; - - } else { - if (strpos($extendType, '://')) { - $path = $extendType; - } elseif (empty($extendConfig['context'])) { - $path = isset($this->search[$extendType]) ? $this->search[$extendType] : "blueprints://{$extendType}"; - } else { - $separator = $extendConfig['context'][strlen($extendConfig['context'])-1] === '/' ? '' : '/'; - $path = $extendConfig['context'] . $separator . $extendType; - } - if (!preg_match('/\.yaml$/', $path)) { - $path .= '.yaml'; - } - - if (strpos($path, '://')) { - /** @var UniformResourceLocator $locator */ - $locator = Grav::instance()['locator']; - - $files = $locator->findResources($path); - } else { - $files = (array) $path; - } - } - - if ($files) { - $data = array_merge($data, $this->loadBlueprints($files)); - } - } - - return $data; + return $blueprint->init(); } }