<?php
/*
* This file is part of Sulu.
*
* (c) Sulu GmbH
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Sulu\Component\Content\Metadata\Factory;
use Doctrine\Common\Inflector\Inflector;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Resource\FileResource;
/**
* Create new (mapped) structures using the provided loader.
*/
class StructureMetadataFactory implements StructureMetadataFactoryInterface
{
/**
* @var array
*/
private $typePaths = [];
/**
* @var string
*/
private $cachePath;
/**
* @var LoaderInterface
*/
private $loader;
/**
* @var bool
*/
private $debug;
/**
* @var array
*/
private $defaultTypes;
/**
* @var array
*/
private $cache = [];
public function __construct(
LoaderInterface $loader,
array $typePaths,
array $defaultTypes,
$cachePath,
$debug = false
) {
$this->typePaths = $typePaths;
$this->cachePath = $cachePath;
$this->loader = $loader;
$this->debug = $debug;
$this->defaultTypes = $defaultTypes;
}
public function getStructureMetadata($type, $structureType = null)
{
$cacheKey = $type . $structureType;
if (isset($this->cache[$cacheKey])) {
return $this->cache[$cacheKey];
}
$this->assertExists($type);
if (!$structureType) {
$structureType = $this->getDefaultStructureType($type);
}
if (!$structureType) {
return;
}
$cachePath = sprintf(
'%s%s%s%s',
$this->cachePath,
DIRECTORY_SEPARATOR,
Inflector::camelize($type),
Inflector::camelize($structureType)
);
$cache = new ConfigCache($cachePath, $this->debug);
if ($this->debug || !$cache->isFresh()) {
$paths = $this->getPaths($type);
// reverse paths, so that the last path overrides previous ones
$fileLocator = new FileLocator(array_reverse($paths));
try {
$filePath = $fileLocator->locate(sprintf('%s.xml', $structureType));
} catch (\InvalidArgumentException $e) {
throw new Exception\StructureTypeNotFoundException(
sprintf(
'Could not load structure type "%s" for document type "%s", looked in "%s"',
$structureType,
$type,
implode('", "', $paths)
), null, $e
);
}
$metadata = $this->loader->load($filePath, $type);
$resources = [new FileResource($filePath)];
$cache->write(
serialize($metadata),
$resources
);
}
$structure = unserialize(file_get_contents($cachePath));
$this->cache[$cacheKey] = $structure;
return $structure;
}
public function getStructures($type)
{
$structureNames = $this->getStructureNames($type);
$structures = [];
foreach ($structureNames as $structureName) {
$structures[] = $this->getStructureMetadata($type, $structureName);
}
return $structures;
}
public function getStructureTypes(): array
{
return array_keys($this->typePaths);
}
public function hasStructuresFor($type)
{
return isset($this->typePaths[$type]);
}
/**
* Return the structure names for the given type
* (not necessarily valid).
*
* @param string $type
*
* @return string[]
*/
private function getStructureNames($type)
{
$this->assertExists($type);
$structureNames = [];
foreach ($this->typePaths[$type] as $pathConfig) {
$structurePath = $pathConfig['path'];
// Ignore not-existing paths
if (!file_exists($structurePath)) {
continue;
}
$iterator = new \DirectoryIterator($structurePath);
foreach ($iterator as $file) {
$ext = $file->getExtension();
if ('xml' !== $ext) {
continue;
}
$structureNames[] = $file->getBasename('.' . $ext);
}
}
return $structureNames;
}
/**
* Assert type exists.
*
* @param string $type
*/
private function assertExists($type)
{
if (!isset($this->typePaths[$type])) {
throw new Exception\DocumentTypeNotFoundException(
sprintf(
'Structure path for document type "%s" is not mapped. Mapped structure types: "%s"',
$type,
implode('", "', array_keys($this->typePaths))
)
);
}
}
/**
* Get the paths from the type path configuration.
*
* @param string $type
*
* @return array
*/
private function getPaths($type)
{
$typeConfigs = $this->typePaths[$type];
$paths = [];
foreach ($typeConfigs as $typeConfig) {
$paths[] = $typeConfig['path'];
}
return $paths;
}
/**
* Return the default structure type for the the given document type.
*
* @param string $type
*
* @return string|null
*/
private function getDefaultStructureType($type)
{
if (!isset($this->defaultTypes[$type])) {
return;
}
return $this->defaultTypes[$type];
}
}