diff --git a/Classes/Domain/Model/Component.php b/Classes/Domain/Model/Component.php new file mode 100644 index 0000000..2fc4055 --- /dev/null +++ b/Classes/Domain/Model/Component.php @@ -0,0 +1,89 @@ +namespace = $fullNamespace; + $this->package = $package; + $this->name = $name; + $this->file = $file; + } + + public function getNamespace(): string + { + return $this->namespace; + } + + public function getPackage(): string + { + return $this->package; + } + + public function getName(): string + { + return $this->name; + } + + public function getPath(): string + { + return dirname($this->file); + } + + public function getFile(): string + { + return $this->file; + } + + public function getData(): array + { + return $this->data; + } + + public function setData(array $data): void + { + $this->data = $data; + } + + public function getClass(): ?string + { + return $this->class; + } + + public function setClass(string $class): void + { + $this->class = $class; + } + + public function getPrefix(): ?string + { + return $this->prefix; + } + + public function setPrefix(string $prefix): void + { + $this->prefix = $prefix; + } + + public function __toString(): string + { + return $this->file; + } +} diff --git a/Classes/Fluid/ViewHelper/ComponentRenderer.php b/Classes/Fluid/ViewHelper/ComponentRenderer.php index cec7023..9f22193 100644 --- a/Classes/Fluid/ViewHelper/ComponentRenderer.php +++ b/Classes/Fluid/ViewHelper/ComponentRenderer.php @@ -4,14 +4,14 @@ use Psr\Container\ContainerInterface; use SMS\FluidComponents\Domain\Model\RequiredSlotPlaceholder; +use SMS\FluidComponents\Domain\Model\Component; use SMS\FluidComponents\Domain\Model\Slot; use SMS\FluidComponents\Interfaces\ComponentAware; use SMS\FluidComponents\Interfaces\EscapedParameter; use SMS\FluidComponents\Interfaces\RenderingContextAware; use SMS\FluidComponents\Utility\ComponentArgumentConverter; +use SMS\FluidComponents\Service\ComponentDataLoader; use SMS\FluidComponents\Utility\ComponentLoader; -use SMS\FluidComponents\Utility\ComponentPrefixer\ComponentPrefixerInterface; -use SMS\FluidComponents\Utility\ComponentPrefixer\GenericComponentPrefixer; use SMS\FluidComponents\Utility\ComponentSettings; use SMS\FluidComponents\ViewHelpers\ComponentViewHelper; use SMS\FluidComponents\ViewHelpers\ContentViewHelper; @@ -49,6 +49,13 @@ class ComponentRenderer extends AbstractViewHelper */ protected $componentNamespace; + /** + * Object containing information about the current component + * + * @var Component + */ + protected Component $component; + /** * Cache for component template instance used for rendering * @@ -66,13 +73,6 @@ class ComponentRenderer extends AbstractViewHelper */ protected static $componentArgumentDefinitionCache = []; - /** - * Cache of component prefixer objects - * - * @var array - */ - protected static $componentPrefixerCache = []; - /** * Components are HTML markup which should not be escaped * @@ -97,6 +97,11 @@ class ComponentRenderer extends AbstractViewHelper */ protected ComponentSettings $componentSettings; + /** + * @var ComponentDataLoader + */ + protected ComponentDataLoader $componentDataLoader; + /** * @var ComponentArgumentConverter */ @@ -116,11 +121,13 @@ class ComponentRenderer extends AbstractViewHelper public function __construct( ComponentLoader $componentLoader, ComponentSettings $componentSettings, + ComponentDataLoader $componentDataLoader, ComponentArgumentConverter $componentArgumentConverter, ContainerInterface $container ) { $this->componentLoader = $componentLoader; $this->componentSettings = $componentSettings; + $this->componentDataLoader = $componentDataLoader; $this->componentArgumentConverter = $componentArgumentConverter; $this->container = $container; } @@ -134,39 +141,20 @@ public function __construct( public function setComponentNamespace($componentNamespace) { $this->componentNamespace = $componentNamespace; + $this->component = $this->componentLoader->findComponent($this->componentNamespace); return $this; } /** * Returns the namespace of the component the viewhelper renders * - * @return void + * @return string */ public function getComponentNamespace() { return $this->componentNamespace; } - /** - * Returns the component prefix - * - * @return string - */ - public function getComponentClass() - { - return $this->getComponentPrefixer()->prefix($this->componentNamespace); - } - - /** - * Returns the component prefix - * - * @return string - */ - public function getComponentPrefix() - { - return $this->getComponentClass() . $this->getComponentPrefixer()->getSeparator(); - } - /** * Renders the component the viewhelper is responsible for * TODO this can probably be improved by using renderComponent() directly @@ -193,12 +181,11 @@ public function render() } $variableContainer = $renderingContext->getVariableProvider(); + // Load additional data for component + $this->componentDataLoader->loadData($this->component); + // Provide information about component to renderer - $variableContainer->add('component', [ - 'namespace' => $this->componentNamespace, - 'class' => $this->getComponentClass(), - 'prefix' => $this->getComponentPrefix(), - ]); + $variableContainer->add('component', $this->component); $variableContainer->add('settings', $this->componentSettings); // Provide supplied arguments from component call to renderer @@ -228,12 +215,10 @@ public function render() // Initialize component rendering template if (!isset($this->parsedTemplate)) { - $componentFile = $this->componentLoader->findComponent($this->componentNamespace); - $this->parsedTemplate = $renderingContext->getTemplateParser()->getOrParseAndStoreTemplate( - $this->getTemplateIdentifier($componentFile), - function () use ($componentFile) { - return file_get_contents($componentFile); + $this->getTemplateIdentifier($this->component->getFile()), + function () { + return file_get_contents($this->component->getFile()); } ); } @@ -459,12 +444,10 @@ protected function initializeComponentParams() { $renderingContext = $this->getRenderingContext(); - $componentFile = $this->componentLoader->findComponent($this->componentNamespace); - // Parse component template without using the cache $parsedTemplate = $renderingContext->getTemplateParser()->parse( - file_get_contents($componentFile), - $this->getTemplateIdentifier($componentFile) + file_get_contents($this->component->getFile()), + $this->getTemplateIdentifier($this->component->getFile()) ); // Extract all component viewhelpers @@ -476,7 +459,7 @@ protected function initializeComponentParams() if (count($componentNodes) > 1) { throw new Exception(sprintf( 'Only one component per file allowed in: %s', - $componentFile + $this->component->getFile() ), 1527779393); } @@ -607,50 +590,6 @@ protected function getTemplateIdentifier(string $pathAndFilename, string $prefix ); } - /** - * Returns the prefixer object responsible for the current component namespaces - * - * @return ComponentPrefixerInterface - */ - protected function getComponentPrefixer() - { - if (!isset(self::$componentPrefixerCache[$this->componentNamespace])) { - if (isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['prefixer']) && - is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['prefixer']) - ) { - arsort($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['prefixer']); - foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['prefixer'] as $namespace => $prefixer) { - $namespace = ltrim($namespace, '\\'); - if (strpos($this->componentNamespace, $namespace) === 0) { - $componentPrefixerClass = $prefixer; - break; - } - } - } - - if (empty($componentPrefixerClass)) { - $componentPrefixerClass = GenericComponentPrefixer::class; - } - - if ($this->container->has($componentPrefixerClass)) { - $componentPrefixer = $this->container->get($componentPrefixerClass); - } else { - $componentPrefixer = GeneralUtility::makeInstance($componentPrefixerClass); - } - - if (!($componentPrefixer instanceof ComponentPrefixerInterface)) { - throw new Exception(sprintf( - 'Invalid component prefixer: %s', - $componentPrefixerClass - ), 1530608357); - } - - self::$componentPrefixerCache[$this->componentNamespace] = $componentPrefixer; - } - - return self::$componentPrefixerCache[$this->componentNamespace]; - } - /** * @return RenderingContext */ diff --git a/Classes/Interfaces/ComponentDataProvider.php b/Classes/Interfaces/ComponentDataProvider.php new file mode 100644 index 0000000..a06618e --- /dev/null +++ b/Classes/Interfaces/ComponentDataProvider.php @@ -0,0 +1,10 @@ +dataProviders = $dataProviders; + } + + public function reset(): void + { + $this->loadedComponents = []; + } + + public function loadData(Component $component): void + { + if (isset($this->loadedComponents[$component->getNamespace()])) { + return; + } + + foreach ($this->dataProviders as $dataProvider) { + $dataProvider->applyData($component); + } + $this->loadedComponents[$component->getNamespace()] = true; + } +} diff --git a/Classes/Service/DataProvider/ComponentPrefixer.php b/Classes/Service/DataProvider/ComponentPrefixer.php new file mode 100644 index 0000000..5cd50b7 --- /dev/null +++ b/Classes/Service/DataProvider/ComponentPrefixer.php @@ -0,0 +1,25 @@ +getPrefix() !== null) { + return; + } + + $vendorName = substr($component->getPackage(), 0, strpos($component->getPackage(), '\\')); + $componentName = str_replace('\\', '', $component->getName()); + $prefix = strtolower($vendorName) . $componentName; + + $component->setClass($prefix); + $component->setPrefix($prefix . '_'); + } +} diff --git a/Classes/Service/DataProvider/LegacyComponentPrefixer.php b/Classes/Service/DataProvider/LegacyComponentPrefixer.php new file mode 100644 index 0000000..27163d9 --- /dev/null +++ b/Classes/Service/DataProvider/LegacyComponentPrefixer.php @@ -0,0 +1,79 @@ +setConfiguration($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['prefixer']); + } + } + + public function setConfiguration(array $configuration) + { + $this->configuration = $configuration; + } + + public function applyData(Component $component): void + { + $componentPrefixer = $this->getPrefixerForNamespace($component->getNamespace()); + if (!$componentPrefixer) { + return; + } + + $prefix = $componentPrefixer->prefix($component->getNamespace()); + $component->setClass($prefix); + $component->setPrefix($prefix . $componentPrefixer->getSeparator()); + } + + private function getPrefixerForNamespace(string $namespace): ?ComponentPrefixerInterface + { + $componentPrefixer = null; + foreach ($this->configuration as $targetedNamespace => $className) { + $targetedNamespace = ltrim($targetedNamespace, '\\'); + if (strpos($namespace, $targetedNamespace) !== 0) { + continue; + } + + if ($this->container->has($className)) { + $componentPrefixer = $this->container->get($className); + } else { + $componentPrefixer = GeneralUtility::makeInstance($className); + } + + if (!($componentPrefixer instanceof ComponentPrefixerInterface)) { + throw new \TYPO3Fluid\Fluid\Core\ViewHelper\Exception( + sprintf('Invalid component prefixer: %s', $className), + 1530608357 + ); + } + } + return $componentPrefixer; + } + + public function injectContainer(ContainerInterface $container): void + { + $this->container = $container; + } +} diff --git a/Classes/Utility/ComponentLoader.php b/Classes/Utility/ComponentLoader.php index 259bc63..bbefa1d 100644 --- a/Classes/Utility/ComponentLoader.php +++ b/Classes/Utility/ComponentLoader.php @@ -2,6 +2,8 @@ namespace SMS\FluidComponents\Utility; +use SMS\FluidComponents\Domain\Model\Component; + class ComponentLoader implements \TYPO3\CMS\Core\SingletonInterface { /** @@ -92,7 +94,7 @@ public function getNamespaces(): array * * @param string $class * @param string $ext - * @return string|null + * @return Component|null */ public function findComponent(string $class, string $ext = '.html') { @@ -110,15 +112,17 @@ public function findComponent(string $class, string $ext = '.html') continue; } - $componentParts = explode('\\', trim(substr($class, strlen($namespace)), '\\')); + $componentName = trim(substr($class, strlen($namespace)), '\\'); + $componentParts = explode('\\', $componentName); $componentPath = $path . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $componentParts); $componentFile = $componentPath . DIRECTORY_SEPARATOR . end($componentParts) . $ext; // Check if component file exists if (file_exists($componentFile)) { - $this->componentsCache[$cacheIdentifier] = $componentFile; - return $componentFile; + $component = new Component($class, $namespace, $componentName, $componentFile); + $this->componentsCache[$cacheIdentifier] = $component; + return $component; } } diff --git a/Classes/Utility/ComponentPrefixer/ComponentPrefixerInterface.php b/Classes/Utility/ComponentPrefixer/ComponentPrefixerInterface.php index 69a56eb..0268acc 100644 --- a/Classes/Utility/ComponentPrefixer/ComponentPrefixerInterface.php +++ b/Classes/Utility/ComponentPrefixer/ComponentPrefixerInterface.php @@ -2,6 +2,9 @@ namespace SMS\FluidComponents\Utility\ComponentPrefixer; +/** + * @deprecated Use ComponentDataProvider instead + */ interface ComponentPrefixerInterface extends \TYPO3\CMS\Core\SingletonInterface { /** diff --git a/Classes/Utility/ComponentPrefixer/GenericComponentPrefixer.php b/Classes/Utility/ComponentPrefixer/GenericComponentPrefixer.php index 085ac07..13c5430 100644 --- a/Classes/Utility/ComponentPrefixer/GenericComponentPrefixer.php +++ b/Classes/Utility/ComponentPrefixer/GenericComponentPrefixer.php @@ -4,6 +4,9 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; +/** + * @deprecated Use ComponentDataProvider instead + */ class GenericComponentPrefixer implements ComponentPrefixerInterface { /** diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index d329e28..b704b59 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -8,6 +8,16 @@ services: resource: '../Classes/*' exclude: '../Classes/Domain/Model/*' + SMS\FluidComponents\Service\DataProvider\ComponentPrefixer: + tags: ['fluidcomponents.dataprovider'] + + SMS\FluidComponents\Service\DataProvider\LegacyComponentPrefixer: + tags: ['fluidcomponents.dataprovider'] + + SMS\FluidComponents\Service\ComponentDataLoader: + arguments: + - !tagged_iterator 'fluidcomponents.dataprovider' + SMS\FluidComponents\Command\GenerateXsdCommand: tags: - name: 'console.command'