TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Fluid\Core\Cache;
|
||||
|
||||
use TYPO3\CMS\Core\Cache\Exception\InvalidDataException;
|
||||
use TYPO3\CMS\Core\Cache\Frontend\PhpFrontend;
|
||||
use TYPO3Fluid\Fluid\Core\Cache\FluidCacheInterface;
|
||||
|
||||
/**
|
||||
* Connector class that enables the TYPO3 cache called "fluid_template" to be operated with the
|
||||
* interface appropriate for the Fluid engine.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class FluidTemplateCache extends PhpFrontend implements FluidCacheInterface
|
||||
{
|
||||
/**
|
||||
* @param null $name
|
||||
*/
|
||||
public function flush($name = null): void
|
||||
{
|
||||
parent::flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $entryIdentifier
|
||||
*/
|
||||
public function get($entryIdentifier): mixed
|
||||
{
|
||||
return $this->requireOnce($entryIdentifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $entryIdentifier
|
||||
* @param string $sourceCode
|
||||
* @param int $lifetime
|
||||
* @throws InvalidDataException
|
||||
*/
|
||||
public function set($entryIdentifier, $sourceCode, array $tags = [], $lifetime = null): void
|
||||
{
|
||||
if (str_starts_with($sourceCode, '<?php')) {
|
||||
// Remove opening PHP tag; it is added by the cache backend to which
|
||||
// we delegate and would be duplicated if not removed.
|
||||
$sourceCode = substr($sourceCode, 6);
|
||||
}
|
||||
parent::set($entryIdentifier, $sourceCode, $tags, time() + 86400);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Fluid\Core\Component;
|
||||
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
|
||||
|
||||
/**
|
||||
* @internal May change / vanish any time
|
||||
*/
|
||||
final readonly class ComponentCollectionRegistry
|
||||
{
|
||||
/**
|
||||
* @var array<string, DeclarativeComponentCollection>
|
||||
*/
|
||||
private array $componentCollections;
|
||||
|
||||
public function __construct(
|
||||
#[Autowire(service: 'cache.fluid_component_definitions')]
|
||||
private FrontendInterface $componentDefinitionsCache,
|
||||
private EventDispatcherInterface $eventDispatcher,
|
||||
#[Autowire(service: 'fluid.component.collections')]
|
||||
iterable $componentCollectionsConfig,
|
||||
) {
|
||||
$componentCollections = [];
|
||||
foreach ($componentCollectionsConfig as $namespace => $config) {
|
||||
$componentCollections[$namespace] = $this->createComponentCollectionObject($namespace, $config);
|
||||
}
|
||||
$this->componentCollections = $componentCollections;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, DeclarativeComponentCollection>
|
||||
*/
|
||||
public function getAll(): array
|
||||
{
|
||||
return $this->componentCollections;
|
||||
}
|
||||
|
||||
private function createComponentCollectionObject(string $namespace, array $config): DeclarativeComponentCollection
|
||||
{
|
||||
if (!isset($config['templatePaths']) || !is_array($config['templatePaths']) || $config['templatePaths'] === []) {
|
||||
throw new \RuntimeException(sprintf(
|
||||
'Invalid or empty template paths provided for Fluid component collection "%s". At least one template path needs to be specified in Configuration/Fluid/ComponentCollections.php.',
|
||||
$namespace,
|
||||
), 1768473237);
|
||||
}
|
||||
$componentCollection = new DeclarativeComponentCollection(
|
||||
$this->componentDefinitionsCache,
|
||||
$this->eventDispatcher,
|
||||
$namespace,
|
||||
$config['templatePaths'],
|
||||
);
|
||||
if (array_key_exists('templateNamePattern', $config)) {
|
||||
$componentCollection = $componentCollection->withTemplateNamePattern($config['templateNamePattern']);
|
||||
}
|
||||
if (array_key_exists('additionalArgumentsAllowed', $config)) {
|
||||
$componentCollection = $componentCollection->withAdditionalArgumentsAllowed($config['additionalArgumentsAllowed']);
|
||||
}
|
||||
return $componentCollection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Fluid\Core\Component;
|
||||
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
||||
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Fluid\Event\ModifyComponentDefinitionEvent;
|
||||
use TYPO3\CMS\Fluid\Event\ProvideStaticVariablesToComponentEvent;
|
||||
use TYPO3\CMS\Fluid\View\TemplatePaths;
|
||||
use TYPO3Fluid\Fluid\Core\Component\ComponentAdapter;
|
||||
use TYPO3Fluid\Fluid\Core\Component\ComponentDefinition;
|
||||
use TYPO3Fluid\Fluid\Core\Component\ComponentDefinitionProviderInterface;
|
||||
use TYPO3Fluid\Fluid\Core\Component\ComponentListProviderInterface;
|
||||
use TYPO3Fluid\Fluid\Core\Component\ComponentRendererInterface;
|
||||
use TYPO3Fluid\Fluid\Core\Component\ComponentTemplateResolverInterface;
|
||||
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContext;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\TemplateStructureViewHelperResolver;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\UnresolvableViewHelperException;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperResolverDelegateInterface;
|
||||
use TYPO3Fluid\Fluid\View\Exception\InvalidTemplateResourceException;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
#[Autoconfigure(autowire: false)]
|
||||
final readonly class DeclarativeComponentCollection implements ViewHelperResolverDelegateInterface, ComponentDefinitionProviderInterface, ComponentTemplateResolverInterface, ComponentListProviderInterface
|
||||
{
|
||||
private string $templateNamePattern;
|
||||
|
||||
public function __construct(
|
||||
private FrontendInterface $cache,
|
||||
private EventDispatcherInterface $eventDispatcher,
|
||||
private string $namespace,
|
||||
private array $templatePaths,
|
||||
string $templateNamePattern = '{path}/{name}/{name}',
|
||||
private bool $additionalArgumentsAllowed = false,
|
||||
) {
|
||||
$this->templateNamePattern = trim($templateNamePattern, '/');
|
||||
}
|
||||
|
||||
public function withTemplateNamePattern(string $templateNamePattern): static
|
||||
{
|
||||
return new static($this->cache, $this->eventDispatcher, $this->namespace, $this->templatePaths, $templateNamePattern, $this->additionalArgumentsAllowed);
|
||||
}
|
||||
|
||||
public function withAdditionalArgumentsAllowed(bool $additionalArgumentsAllowed): static
|
||||
{
|
||||
return new static($this->cache, $this->eventDispatcher, $this->namespace, $this->templatePaths, $this->templateNamePattern, $additionalArgumentsAllowed);
|
||||
}
|
||||
|
||||
public function resolveTemplateName(string $viewHelperName): string
|
||||
{
|
||||
$fragments = array_map(ucfirst(...), explode('.', $viewHelperName));
|
||||
$name = array_pop($fragments);
|
||||
$path = implode('/', $fragments);
|
||||
return ltrim(str_replace(['{path}', '{name}'], [$path, $name], $this->templateNamePattern), '/');
|
||||
}
|
||||
|
||||
public function getAvailableComponents(): array
|
||||
{
|
||||
$availableTemplates = $this->getTemplatePaths()->resolveAvailableTemplateFiles(null, null, true);
|
||||
$templateNamePattern = self::convertTemplatePatternToRegularExpression($this->templateNamePattern);
|
||||
$availableComponents = [];
|
||||
foreach ($availableTemplates as $templatePath) {
|
||||
// Remove template root path
|
||||
foreach ($this->getTemplatePaths()->getTemplateRootPaths() as $rootPath) {
|
||||
if (str_starts_with($templatePath, $rootPath)) {
|
||||
$templatePath = substr($templatePath, strlen($rootPath));
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Convert template name into ViewHelper name and validate directory structure
|
||||
// (resolveTemplateName() in reverse)
|
||||
if (!preg_match($templateNamePattern, $templatePath, $matches)) {
|
||||
continue;
|
||||
}
|
||||
$fragments = $matches['path'] ? GeneralUtility::trimExplode('/', $matches['path'], true) : [];
|
||||
$fragments[] = $matches['name'];
|
||||
$availableComponents[] = implode('.', array_map(lcfirst(...), $fragments));
|
||||
}
|
||||
return array_values(array_unique($availableComponents));
|
||||
}
|
||||
|
||||
public function getTemplatePaths(): TemplatePaths
|
||||
{
|
||||
$templatePaths = new TemplatePaths();
|
||||
$templatePaths->setTemplateRootPaths($this->templatePaths);
|
||||
return $templatePaths;
|
||||
}
|
||||
|
||||
public function getAdditionalVariables(string $viewHelperName): array
|
||||
{
|
||||
// Allow to provide additional variables to the component template.
|
||||
// Note that this deliberately cannot depend on runtime characteristics,
|
||||
// such as the request, as this should be done in the renderer.
|
||||
$event = $this->eventDispatcher->dispatch(
|
||||
new ProvideStaticVariablesToComponentEvent($this, $viewHelperName)
|
||||
);
|
||||
return $event->getStaticVariables();
|
||||
}
|
||||
|
||||
public function getComponentDefinition(string $viewHelperName): ComponentDefinition
|
||||
{
|
||||
$cacheIdentifier = hash('xxh3', $this->namespace . '--' . $viewHelperName);
|
||||
$componentDefinition = $this->cache->get($cacheIdentifier);
|
||||
if ($componentDefinition instanceof ComponentDefinition) {
|
||||
return $componentDefinition;
|
||||
}
|
||||
$templateName = $this->resolveTemplateName($viewHelperName);
|
||||
/**
|
||||
* Extract component definition from component template
|
||||
* This part is an ugly workaround because of shortcomings in the Fluid parser.
|
||||
* Once this has been resolved on the Fluid side, there will most likely be a better API.
|
||||
* @see \TYPO3Fluid\Fluid\Core\Component\AbstractComponentCollection
|
||||
*/
|
||||
$renderingContext = new RenderingContext();
|
||||
$renderingContext->setViewHelperResolver(new TemplateStructureViewHelperResolver());
|
||||
$parsedTemplate = $renderingContext->getTemplateParser()->parse(
|
||||
$this->getTemplatePaths()->getTemplateSource('Default', $templateName),
|
||||
$this->getTemplatePaths()->getTemplateIdentifier('Default', $templateName),
|
||||
$this->getTemplatePaths()->resolveTemplateFileForControllerAndActionAndFormat('Default', $templateName),
|
||||
);
|
||||
$componentDefinition = new ComponentDefinition(
|
||||
$viewHelperName,
|
||||
$parsedTemplate->getArgumentDefinitions(),
|
||||
$this->additionalArgumentsAllowed,
|
||||
$parsedTemplate->getAvailableSlots(),
|
||||
);
|
||||
// Allow modification of component definition before it's written to cache.
|
||||
// Note that this deliberately cannot depend on runtime characteristics,
|
||||
// such as the request, as this should be done during rendering (e. g. by allowing
|
||||
// arbitrary arguments)
|
||||
$event = $this->eventDispatcher->dispatch(
|
||||
new ModifyComponentDefinitionEvent($this->namespace, $componentDefinition)
|
||||
);
|
||||
$componentDefinition = $event->getComponentDefinition();
|
||||
$this->cache->set($cacheIdentifier, $componentDefinition);
|
||||
return $componentDefinition;
|
||||
}
|
||||
|
||||
public function getComponentRenderer(): ComponentRendererInterface
|
||||
{
|
||||
return new EventBasedComponentRenderer($this->eventDispatcher, $this);
|
||||
}
|
||||
|
||||
public function resolveViewHelperClassName(string $name): string
|
||||
{
|
||||
$expectedTemplateName = $this->resolveTemplateName($name);
|
||||
try {
|
||||
$this->getTemplatePaths()->resolveTemplateFileForControllerAndActionAndFormat('Default', $expectedTemplateName, null, true);
|
||||
} catch (InvalidTemplateResourceException $e) {
|
||||
throw new UnresolvableViewHelperException(sprintf(
|
||||
'The component template "%s" in format ".%s" could not be found in the configured template paths. %s',
|
||||
$expectedTemplateName,
|
||||
$this->getTemplatePaths()->getFormat(),
|
||||
$e->evaluatedTemplatePaths !== [] ? 'The following file paths were evaluated: "' . implode('", "', $e->evaluatedTemplatePaths) . '"' : 'No paths configured.',
|
||||
), 1765711586);
|
||||
}
|
||||
return ComponentAdapter::class;
|
||||
}
|
||||
|
||||
public function getNamespace(): string
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
private static function convertTemplatePatternToRegularExpression(string $templateNamePattern): string
|
||||
{
|
||||
$delimiter = '~';
|
||||
$pathMarker = preg_quote('{path}/', $delimiter);
|
||||
$nameMarker = preg_quote('{name}', $delimiter);
|
||||
$templateNamePattern = preg_quote($templateNamePattern, $delimiter);
|
||||
if (str_contains($templateNamePattern, $pathMarker)) {
|
||||
[$beforePath, $afterPath] = explode($pathMarker, $templateNamePattern, 2);
|
||||
$templateNamePattern = $beforePath . '(?<path>(?:.+?/)?)' . str_replace($pathMarker, '(?P=path)', $afterPath);
|
||||
}
|
||||
if (str_contains($templateNamePattern, $nameMarker)) {
|
||||
[$beforeName, $afterName] = explode($nameMarker, $templateNamePattern, 2);
|
||||
$templateNamePattern = $beforeName . '(?<name>[^/]+?)' . str_replace($nameMarker, '(?P=name)', $afterName);
|
||||
}
|
||||
return $delimiter . '^' . $templateNamePattern . '$' . $delimiter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Fluid\Core\Component;
|
||||
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
||||
use TYPO3\CMS\Fluid\Event\RenderComponentEvent;
|
||||
use TYPO3Fluid\Fluid\Core\Component\ComponentDefinitionProviderInterface;
|
||||
use TYPO3Fluid\Fluid\Core\Component\ComponentRenderer as FluidComponentRenderer;
|
||||
use TYPO3Fluid\Fluid\Core\Component\ComponentRendererInterface;
|
||||
use TYPO3Fluid\Fluid\Core\Component\ComponentTemplateResolverInterface;
|
||||
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperResolverDelegateInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
#[Autoconfigure(autowire: false)]
|
||||
final readonly class EventBasedComponentRenderer implements ComponentRendererInterface
|
||||
{
|
||||
public function __construct(
|
||||
private EventDispatcherInterface $eventDispatcher,
|
||||
private ViewHelperResolverDelegateInterface&ComponentDefinitionProviderInterface&ComponentTemplateResolverInterface $componentCollection,
|
||||
) {}
|
||||
|
||||
public function renderComponent(string $viewHelperName, array $arguments, array $slots, RenderingContextInterface $parentRenderingContext): string
|
||||
{
|
||||
$request
|
||||
= $parentRenderingContext->hasAttribute(ServerRequestInterface::class)
|
||||
? $parentRenderingContext->getAttribute(ServerRequestInterface::class)
|
||||
: null;
|
||||
$event = $this->eventDispatcher->dispatch(
|
||||
new RenderComponentEvent($this->componentCollection, $viewHelperName, $arguments, $slots, $parentRenderingContext, $request)
|
||||
);
|
||||
if ($event->getRenderedComponent() !== null) {
|
||||
return $event->getRenderedComponent();
|
||||
}
|
||||
return (new FluidComponentRenderer($this->componentCollection))->renderComponent(
|
||||
$viewHelperName,
|
||||
$event->getArguments(),
|
||||
$event->getSlots(),
|
||||
$parentRenderingContext,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Fluid\Core\Rendering;
|
||||
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Fluid\Core\ViewHelper\ViewHelperResolver;
|
||||
use TYPO3\CMS\Fluid\View\TemplatePaths;
|
||||
use TYPO3Fluid\Fluid\Core\Cache\FluidCacheInterface;
|
||||
use TYPO3Fluid\Fluid\Core\Compiler\TemplateCompiler;
|
||||
use TYPO3Fluid\Fluid\Core\Parser\Configuration;
|
||||
use TYPO3Fluid\Fluid\Core\Parser\InterceptorInterface;
|
||||
use TYPO3Fluid\Fluid\Core\Parser\TemplateParser;
|
||||
use TYPO3Fluid\Fluid\Core\Variables\StandardVariableProvider;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\ArgumentProcessorInterface;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperInvoker;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperVariableContainer;
|
||||
|
||||
class RenderingContext extends \TYPO3Fluid\Fluid\Core\Rendering\RenderingContext
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $controllerName = 'Default';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $controllerAction = 'Default';
|
||||
|
||||
/**
|
||||
* @internal constructor, use `RenderingContextFactory->create()` instead
|
||||
*/
|
||||
public function __construct(
|
||||
ViewHelperResolver $viewHelperResolver,
|
||||
FluidCacheInterface $cache,
|
||||
array $templateProcessors,
|
||||
array $expressionNodeTypes,
|
||||
TemplatePaths $templatePaths,
|
||||
ArgumentProcessorInterface $argumentProcessor
|
||||
) {
|
||||
// Partially cloning parent::__construct() but with custom implementations.
|
||||
$this->setTemplateParser(new TemplateParser());
|
||||
$this->setTemplateCompiler(new TemplateCompiler());
|
||||
$this->setViewHelperInvoker(new ViewHelperInvoker());
|
||||
$this->setArgumentProcessor($argumentProcessor);
|
||||
$this->setViewHelperVariableContainer(new ViewHelperVariableContainer());
|
||||
$this->setVariableProvider(new StandardVariableProvider());
|
||||
$this->setTemplateProcessors($templateProcessors);
|
||||
$this->setExpressionNodeTypes($expressionNodeTypes);
|
||||
$this->setTemplatePaths($templatePaths);
|
||||
$this->setViewHelperResolver($viewHelperResolver);
|
||||
$this->setCache($cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build parser configuration. Adds custom fluid interceptors from configuration.
|
||||
*
|
||||
* @throws \InvalidArgumentException if a class not implementing InterceptorInterface was registered
|
||||
*/
|
||||
public function buildParserConfiguration(): Configuration
|
||||
{
|
||||
$parserConfiguration = parent::buildParserConfiguration();
|
||||
foreach ($GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['interceptors'] as $className) {
|
||||
$interceptor = GeneralUtility::makeInstance($className);
|
||||
if (!$interceptor instanceof InterceptorInterface) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Interceptor "' . $className . '" needs to implement ' . InterceptorInterface::class . '.',
|
||||
1462869795
|
||||
);
|
||||
}
|
||||
$parserConfiguration->addInterceptor($interceptor);
|
||||
}
|
||||
return $parserConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $action
|
||||
*/
|
||||
public function setControllerAction($action): void
|
||||
{
|
||||
$dotPosition = strpos($action, '.');
|
||||
if ($dotPosition !== false) {
|
||||
$action = substr($action, 0, $dotPosition);
|
||||
}
|
||||
$this->controllerAction = $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $controllerName
|
||||
*/
|
||||
public function setControllerName($controllerName): void
|
||||
{
|
||||
$this->controllerName = $controllerName;
|
||||
}
|
||||
|
||||
public function getControllerName(): string
|
||||
{
|
||||
return $this->controllerName;
|
||||
}
|
||||
|
||||
public function getControllerAction(): string
|
||||
{
|
||||
return $this->controllerAction;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Fluid\Core\Rendering;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Cache\CacheManager;
|
||||
use TYPO3\CMS\Core\DependencyInjection\FailsafeContainer;
|
||||
use TYPO3\CMS\Fluid\Core\ViewHelper\ViewHelperResolverFactoryInterface;
|
||||
use TYPO3\CMS\Fluid\View\TemplatePaths;
|
||||
use TYPO3Fluid\Fluid\Core\Cache\FluidCacheInterface;
|
||||
use TYPO3Fluid\Fluid\Core\Parser\TemplateProcessor\EscapingModifierTemplateProcessor;
|
||||
use TYPO3Fluid\Fluid\Core\Parser\TemplateProcessor\NamespaceDetectionTemplateProcessor;
|
||||
use TYPO3Fluid\Fluid\Core\Parser\TemplateProcessor\PassthroughSourceModifierTemplateProcessor;
|
||||
use TYPO3Fluid\Fluid\Core\Parser\TemplateProcessor\RemoveCommentsTemplateProcessor;
|
||||
use TYPO3Fluid\Fluid\Core\Parser\TemplateProcessorInterface;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\ArgumentProcessorInterface;
|
||||
|
||||
/**
|
||||
* Factory class registered in ServiceProvider to create a RenderingContext.
|
||||
*
|
||||
* This is a low level factory always registered, even in failsafe mode: fluid
|
||||
* is needed in install tool which does not rely on the normal (cached) symfony DI
|
||||
* mechanism - Services.yaml is ignored in failsafe mode.
|
||||
*
|
||||
* A casual failsafe instantiation / injection using ServiceProvider.php wouldn't
|
||||
* need this factory. But the failsafe mechanism is strict and relies on two
|
||||
* things: The service is public: true, this is the case with RenderingContext.
|
||||
* And the service is shared: true - a stateless singleton. This is not true for
|
||||
* RenderingContext, it by definition relies on context and must be created a-new
|
||||
* per fluid parsing instance.
|
||||
*
|
||||
* To allow creating RenderingContext objects in failsafe mode, this factory
|
||||
* is registered as service provider to dynamically prepare instances.
|
||||
*
|
||||
* @internal May change / vanish any time
|
||||
*/
|
||||
final readonly class RenderingContextFactory
|
||||
{
|
||||
public function __construct(
|
||||
private ContainerInterface $container,
|
||||
private CacheManager $cacheManager,
|
||||
private ViewHelperResolverFactoryInterface $viewHelperResolverFactory,
|
||||
private ArgumentProcessorInterface $argumentProcessor,
|
||||
) {}
|
||||
|
||||
public function create(array $templatePathsArray = [], ?ServerRequestInterface $request = null): RenderingContext
|
||||
{
|
||||
/** @var TemplateProcessorInterface[] $processors */
|
||||
$processors = [];
|
||||
|
||||
if ($this->container instanceof FailsafeContainer) {
|
||||
// Load default set of processors in failsafe mode (install tool context)
|
||||
// as custom processors can not be retrieved from the symfony container
|
||||
$processors = [
|
||||
new EscapingModifierTemplateProcessor(),
|
||||
new PassthroughSourceModifierTemplateProcessor(),
|
||||
new NamespaceDetectionTemplateProcessor(),
|
||||
new RemoveCommentsTemplateProcessor(),
|
||||
];
|
||||
} else {
|
||||
foreach ($GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['preProcessors'] as $className) {
|
||||
/** @var TemplateProcessorInterface[] $processors */
|
||||
$processors[] = $this->container->get($className);
|
||||
}
|
||||
}
|
||||
|
||||
$cache = $this->cacheManager->getCache('fluid_template');
|
||||
if (!$cache instanceof FluidCacheInterface) {
|
||||
throw new \RuntimeException('Cache fluid_template must implement FluidCacheInterface', 1623148753);
|
||||
}
|
||||
|
||||
$templatePaths = new TemplatePaths();
|
||||
if (!empty($templatePathsArray['templateRootPaths'])) {
|
||||
$templatePaths->setTemplateRootPaths($templatePathsArray['templateRootPaths']);
|
||||
}
|
||||
if (!empty($templatePathsArray['layoutRootPaths'])) {
|
||||
$templatePaths->setLayoutRootPaths($templatePathsArray['layoutRootPaths']);
|
||||
}
|
||||
if (!empty($templatePathsArray['partialRootPaths'])) {
|
||||
$templatePaths->setPartialRootPaths($templatePathsArray['partialRootPaths']);
|
||||
}
|
||||
|
||||
$renderingContext = new RenderingContext(
|
||||
$this->viewHelperResolverFactory->create(),
|
||||
$cache,
|
||||
$processors,
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['expressionNodeTypes'],
|
||||
$templatePaths,
|
||||
$this->argumentProcessor,
|
||||
);
|
||||
if ($request) {
|
||||
$renderingContext->setAttribute(ServerRequestInterface::class, $request);
|
||||
}
|
||||
return $renderingContext;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Fluid\Core\ViewHelper;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use TYPO3\CMS\Core\DependencyInjection\FailsafeContainer;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperInterface;
|
||||
|
||||
/**
|
||||
* Class whose purpose is dedicated to resolving classes which
|
||||
* can be used as ViewHelpers and ExpressionNodes in Fluid.
|
||||
*
|
||||
* This CMS-specific version of the ViewHelperResolver works
|
||||
* almost exactly like the one from Fluid itself, with the main
|
||||
* differences being that this one supports a legacy mode flag
|
||||
* which when toggled on makes the Fluid parser behave exactly
|
||||
* like it did in the legacy CMS Fluid package.
|
||||
*
|
||||
* In addition to modifying the behavior or the parser when
|
||||
* legacy mode is requested, this ViewHelperResolver is also
|
||||
* made capable of "mixing" two different ViewHelper namespaces
|
||||
* to effectively create aliases for the Fluid core ViewHelpers
|
||||
* to be loaded in the TYPO3\CMS\ViewHelpers scope as well.
|
||||
*
|
||||
* Default ViewHelper namespaces are read from the extension-level
|
||||
* configuration file "Configuration/Fluid/Namespaces.php".
|
||||
*
|
||||
* Extending this array allows third party ViewHelper providers
|
||||
* to automatically add or extend namespaces which then become
|
||||
* available in every Fluid template file without having to
|
||||
* register the namespace.
|
||||
*
|
||||
* @internal This is a helper class which is not considered part of TYPO3's Public API.
|
||||
*/
|
||||
class ViewHelperResolver extends \TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperResolver
|
||||
{
|
||||
protected ContainerInterface $container;
|
||||
|
||||
/**
|
||||
* ViewHelperResolver constructor
|
||||
*
|
||||
* @internal constructor, use `ViewHelperResolverFactory->create()` instead
|
||||
*/
|
||||
public function __construct(ContainerInterface $container, array $namespaces, array $resolverDelegates = [])
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->namespaces = $namespaces;
|
||||
$this->resolverDelegates = $resolverDelegates;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $viewHelperClassName
|
||||
*/
|
||||
public function createViewHelperInstanceFromClassName($viewHelperClassName): ViewHelperInterface
|
||||
{
|
||||
if ($this->container instanceof FailsafeContainer) {
|
||||
// Install tool: makeInstance() resolves via the FailsafeContainer when the VH is
|
||||
// registered there, else via `new`. VHs with required constructor arguments used by
|
||||
// install-tool templates must be wired in install/Classes/ServiceProvider.php.
|
||||
/** @var ViewHelperInterface $viewHelperInstance */
|
||||
$viewHelperInstance = GeneralUtility::makeInstance($viewHelperClassName);
|
||||
return $viewHelperInstance;
|
||||
}
|
||||
|
||||
if ($this->container->has($viewHelperClassName)) {
|
||||
/** @var ViewHelperInterface $viewHelperInstance */
|
||||
$viewHelperInstance = $this->container->get($viewHelperClassName);
|
||||
} else {
|
||||
/** @var ViewHelperInterface $viewHelperInstance */
|
||||
$viewHelperInstance = new $viewHelperClassName();
|
||||
}
|
||||
return $viewHelperInstance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Fluid\Core\ViewHelper;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
||||
use Symfony\Component\DependencyInjection\Attribute\AutowireIterator;
|
||||
use TYPO3\CMS\Fluid\Core\Component\ComponentCollectionRegistry;
|
||||
use TYPO3\CMS\Fluid\Core\Component\DeclarativeComponentCollection;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperResolverDelegateInterface;
|
||||
|
||||
/**
|
||||
* @internal May change / vanish any time
|
||||
*/
|
||||
#[Autoconfigure(public: true)]
|
||||
final readonly class ViewHelperResolverDelegateRegistry
|
||||
{
|
||||
/**
|
||||
* @var array<string, ViewHelperResolverDelegateInterface>
|
||||
*/
|
||||
private array $viewHelperResolverDelegates;
|
||||
|
||||
public function __construct(
|
||||
ComponentCollectionRegistry $componentCollectionRegistry,
|
||||
#[AutowireIterator('fluid.resolverdelegate', exclude: [DeclarativeComponentCollection::class], indexAttribute: 'identifier')]
|
||||
iterable $resolverDelegates,
|
||||
) {
|
||||
$this->viewHelperResolverDelegates = array_replace(
|
||||
iterator_to_array($resolverDelegates),
|
||||
$componentCollectionRegistry->getAll(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, ViewHelperResolverDelegateInterface>
|
||||
*/
|
||||
public function getAll(): array
|
||||
{
|
||||
return $this->viewHelperResolverDelegates;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Fluid\Core\ViewHelper;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use TYPO3\CMS\Fluid\Event\ModifyNamespacesEvent;
|
||||
|
||||
/**
|
||||
* Factory class registered in ServiceProvider to create a ViewHelperResolver.
|
||||
*
|
||||
* Note fluid is a failsafe mode aware extensions since its used in the install
|
||||
* tool. We thus need a ServiceProvider.php to correctly instantiate / inject
|
||||
* these class objects. This would be simple, but ViewHelperResolver has state,
|
||||
* and the failsafe mode expects injected services to not have state.
|
||||
*
|
||||
* So, to retrieve a ViewHelperResolver instance, an instance of this factory
|
||||
* is retrieved instead (which is a singleton), which then creates a 'fresh'
|
||||
* instance of the ViewHelperResolver each time create() is called.
|
||||
*
|
||||
* @internal May change / vanish any time
|
||||
*/
|
||||
final readonly class ViewHelperResolverFactory implements ViewHelperResolverFactoryInterface
|
||||
{
|
||||
public function __construct(
|
||||
private ContainerInterface $container,
|
||||
private EventDispatcherInterface $eventDispatcher,
|
||||
private ?ViewHelperResolverDelegateRegistry $viewHelperResolverDelegateRegistry,
|
||||
private iterable $namespaces,
|
||||
) {}
|
||||
|
||||
public function create(): ViewHelperResolver
|
||||
{
|
||||
$event = $this->eventDispatcher->dispatch(new ModifyNamespacesEvent((array)$this->namespaces));
|
||||
return new ViewHelperResolver(
|
||||
$this->container,
|
||||
$event->getNamespaces(),
|
||||
$this->viewHelperResolverDelegateRegistry instanceof ViewHelperResolverDelegateRegistry ? iterator_to_array($this->viewHelperResolverDelegateRegistry->getAll()) : [],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Fluid\Core\ViewHelper;
|
||||
|
||||
/**
|
||||
* @internal May change / vanish any time. This interface *may* be used to override implementation via
|
||||
* ServiceProvider.php in an own extension. However, ServiceProvider.php itself is *not* API,
|
||||
* so this interface isn't as well. Core *may* break this later again, but will still try not to,
|
||||
* but this is not guaranteed.
|
||||
*/
|
||||
interface ViewHelperResolverFactoryInterface
|
||||
{
|
||||
public function create(): ViewHelperResolver;
|
||||
}
|
||||
Reference in New Issue
Block a user