150 lines
5.3 KiB
PHP
150 lines
5.3 KiB
PHP
<?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\Core\Context;
|
|
|
|
use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException;
|
|
use TYPO3\CMS\Core\Context\Exception\AspectPropertyNotFoundException;
|
|
use TYPO3\CMS\Core\Domain\DateTimeFactory;
|
|
use TYPO3\CMS\Core\SingletonInterface;
|
|
|
|
/**
|
|
* Contains the state of a current page request, to be used when reading
|
|
* information of the current request in which configuration/context
|
|
* it is used.
|
|
*
|
|
* Typically, the current main context is initialized very early within each entry-point application,
|
|
* and is then modified overridden in e.g. PSR-15 middlewares (e.g. authentication, preview settings etc).
|
|
*
|
|
* For most use-cases, the current main context is fetched via GeneralUtility::makeInstance(Context::class),
|
|
* however, if custom settings for a single use-case is necessary, it is recommended to clone the base context:
|
|
*
|
|
* ```
|
|
* $mainContext = GeneralUtility::makeInstance(Context::class);
|
|
* $customContext = clone $mainContext;
|
|
* $customContext->setAspect(GeneralUtility::makeInstance(VisibilityAspect::class, true, true, false))
|
|
* ```
|
|
*
|
|
* ... which in turn can be injected in the various places where TYPO3 uses contexts.
|
|
*
|
|
*
|
|
* Classic aspect names to be used are:
|
|
* - date (DateTimeAspect)
|
|
* - workspace
|
|
* - visibility
|
|
* - frontend.user
|
|
* - backend.user
|
|
* - language
|
|
* - frontend.preview [if EXT:frontend is loaded]
|
|
*/
|
|
class Context implements SingletonInterface
|
|
{
|
|
/**
|
|
* @var AspectInterface[]
|
|
*/
|
|
protected array $aspects = [];
|
|
|
|
/**
|
|
* Checks if an aspect exists in the context
|
|
*/
|
|
public function hasAspect(string $name): bool
|
|
{
|
|
return match ($name) {
|
|
'date', 'visibility', 'backend.user', 'frontend.user', 'workspace', 'language' => true,
|
|
default => isset($this->aspects[$name]),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Returns an aspect, if it is set
|
|
*
|
|
* @throws AspectNotFoundException
|
|
* @return ($name is 'date' ? DateTimeAspect
|
|
* : ($name is 'visibility' ? VisibilityAspect
|
|
* : ($name is 'backend.user' ? UserAspect
|
|
* : ($name is 'frontend.user' ? UserAspect
|
|
* : ($name is 'workspace' ? WorkspaceAspect
|
|
* : ($name is 'language' ? LanguageAspect : AspectInterface))))))
|
|
*/
|
|
public function getAspect(string $name): AspectInterface
|
|
{
|
|
if (!isset($this->aspects[$name])) {
|
|
// Ensure the default aspects are available, this is mostly necessary for tests to not set up everything
|
|
switch ($name) {
|
|
case 'date':
|
|
$this->setAspect('date', new DateTimeAspect(DateTimeFactory::createFromTimestamp($GLOBALS['EXEC_TIME'])));
|
|
break;
|
|
case 'visibility':
|
|
$this->setAspect('visibility', new VisibilityAspect());
|
|
break;
|
|
case 'backend.user':
|
|
$this->setAspect('backend.user', new UserAspect());
|
|
break;
|
|
case 'frontend.user':
|
|
$this->setAspect('frontend.user', new UserAspect());
|
|
break;
|
|
case 'workspace':
|
|
$this->setAspect('workspace', new WorkspaceAspect());
|
|
break;
|
|
case 'language':
|
|
$this->setAspect('language', new LanguageAspect());
|
|
break;
|
|
default:
|
|
throw new AspectNotFoundException('No aspect named "' . $name . '" found.', 1527777641);
|
|
}
|
|
}
|
|
return $this->aspects[$name];
|
|
}
|
|
|
|
/**
|
|
* Returns a property from the aspect, but only if the property is found.
|
|
*
|
|
* @throws AspectNotFoundException
|
|
*/
|
|
public function getPropertyFromAspect(string $name, string $property, mixed $default = null): mixed
|
|
{
|
|
if (!$this->hasAspect($name)) {
|
|
throw new AspectNotFoundException('No aspect named "' . $name . '" found.', 1527777868);
|
|
}
|
|
try {
|
|
return $this->getAspect($name)->get($property);
|
|
} catch (AspectPropertyNotFoundException) {
|
|
return $default;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets an aspect, or overrides an existing aspect if an aspect is already set
|
|
*/
|
|
public function setAspect(string $name, AspectInterface $aspect): void
|
|
{
|
|
$this->aspects[$name] = $aspect;
|
|
}
|
|
|
|
/**
|
|
* @internal Using this method is a sign of a technical debt. It is used by RedirectService,
|
|
* but may vanish any time when this is fixed, and thus internal.
|
|
* In general, Context aspects should never have to be unset.
|
|
* When a middleware has to use this method, it is either located
|
|
* at the wrong position in the chain, or has some other dependency issue.
|
|
*/
|
|
public function unsetAspect(string $name): void
|
|
{
|
|
unset($this->aspects[$name]);
|
|
}
|
|
}
|