216 lines
6.5 KiB
PHP
216 lines
6.5 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\Extbase\Persistence\Generic;
|
|
|
|
use TYPO3\CMS\Core\Context\LanguageAspect;
|
|
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
|
|
|
|
/**
|
|
* The persistence session - acts as a Unit of Work for Extbase persistence framework.
|
|
*
|
|
* Warning: This is a stateful-shared service!
|
|
*
|
|
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
|
*/
|
|
class Session
|
|
{
|
|
protected ObjectStorage $reconstitutedEntities;
|
|
protected ObjectStorage $objectMap;
|
|
|
|
/**
|
|
* @var array<non-empty-string, array<non-empty-string, object>>
|
|
*/
|
|
protected array $identifierMap = [];
|
|
|
|
/**
|
|
* Constructs a new Session
|
|
*/
|
|
public function __construct()
|
|
{
|
|
$this->reconstitutedEntities = new ObjectStorage();
|
|
$this->objectMap = new ObjectStorage();
|
|
}
|
|
|
|
/**
|
|
* Registers data for a reconstituted object.
|
|
*
|
|
* $entityData format is described in
|
|
* "Documentation/PersistenceFramework object data format.txt"
|
|
*/
|
|
public function registerReconstitutedEntity(object $entity): void
|
|
{
|
|
$this->reconstitutedEntities->attach($entity);
|
|
}
|
|
|
|
/**
|
|
* Unregisters data for a reconstituted object
|
|
*/
|
|
public function unregisterReconstitutedEntity(object $entity): void
|
|
{
|
|
if ($this->reconstitutedEntities->contains($entity)) {
|
|
$this->reconstitutedEntities->detach($entity);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns all objects which have been registered as reconstituted
|
|
*/
|
|
public function getReconstitutedEntities(): ObjectStorage
|
|
{
|
|
return $this->reconstitutedEntities;
|
|
}
|
|
|
|
/**
|
|
* Checks whether the given object is known to the identity map
|
|
*/
|
|
public function hasObject(object $object): bool
|
|
{
|
|
return $this->objectMap->contains($object);
|
|
}
|
|
|
|
/**
|
|
* Checks whether the given identifier is known to the identity map
|
|
*
|
|
* @param non-empty-string $identifier
|
|
* @param class-string $className
|
|
*/
|
|
public function hasIdentifier(string $identifier, string $className): bool
|
|
{
|
|
return isset($this->identifierMap[$this->getClassIdentifier($className)][$identifier]);
|
|
}
|
|
|
|
/**
|
|
* Returns the object for the given identifier
|
|
*
|
|
* @param non-empty-string $identifier
|
|
* @param class-string $className
|
|
*/
|
|
public function getObjectByIdentifier(string $identifier, string $className): object
|
|
{
|
|
return $this->identifierMap[$this->getClassIdentifier($className)][$identifier];
|
|
}
|
|
|
|
/**
|
|
* Returns the identifier for the given object from
|
|
* the session, if the object was registered.
|
|
*
|
|
* @return non-empty-string|null
|
|
*/
|
|
public function getIdentifierByObject(object $object): ?string
|
|
{
|
|
if ($this->hasObject($object)) {
|
|
return $this->objectMap[$object];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Register an identifier for an object
|
|
*
|
|
* @param non-empty-string $identifier
|
|
*/
|
|
public function registerObject(object $object, string $identifier): void
|
|
{
|
|
$this->objectMap[$object] = $identifier;
|
|
$this->identifierMap[$this->getClassIdentifier(get_class($object))][$identifier] = $object;
|
|
}
|
|
|
|
/**
|
|
* Unregister an object
|
|
*/
|
|
public function unregisterObject(object $object): void
|
|
{
|
|
unset($this->identifierMap[$this->getClassIdentifier(get_class($object))][$this->objectMap[$object]]);
|
|
$this->objectMap->detach($object);
|
|
}
|
|
|
|
/**
|
|
* Destroy the state of the persistence session and reset
|
|
* all internal data.
|
|
*/
|
|
public function destroy(): void
|
|
{
|
|
$this->identifierMap = [];
|
|
$this->objectMap = new ObjectStorage();
|
|
$this->reconstitutedEntities = new ObjectStorage();
|
|
}
|
|
|
|
/**
|
|
* Objects are stored in the cache with their implementation class name
|
|
* to allow reusing instances of different classes that point to the same implementation
|
|
* Returns a unique class identifier respecting configured implementation class names
|
|
*
|
|
* @param class-string $className
|
|
* @return non-empty-string
|
|
*/
|
|
protected function getClassIdentifier(string $className): string
|
|
{
|
|
return strtolower($className);
|
|
}
|
|
|
|
/**
|
|
* Build a language-aware identifier for the identity map by combining
|
|
* a base identifier with a language content identifier.
|
|
*/
|
|
public function buildIdentifier(string|array $baseIdentifier, ?LanguageAspect $languageAspect = null): string
|
|
{
|
|
if (is_array($baseIdentifier)) {
|
|
$identifier = (string)$baseIdentifier['uid'];
|
|
if (isset($baseIdentifier['_LOCALIZED_UID'])) {
|
|
$identifier .= '_' . $baseIdentifier['_LOCALIZED_UID'];
|
|
}
|
|
$baseIdentifier = $identifier;
|
|
}
|
|
// Use default language context for newly inserted objects
|
|
$languageAspect ??= new LanguageAspect(0, 0, LanguageAspect::OVERLAYS_ON_WITH_FLOATING, []);
|
|
return $baseIdentifier . '@' . $this->getContentIdentifier($languageAspect);
|
|
}
|
|
|
|
/**
|
|
* Build a unique identifier representing the content-fetching configuration
|
|
* of the given LanguageAspect.
|
|
*
|
|
* This includes contentId, overlayType, and fallbackChain — everything
|
|
* that affects which record overlay is returned. The language ID is
|
|
* intentionally excluded because it only affects menus/links, not content.
|
|
*
|
|
* @internal
|
|
*/
|
|
protected function getContentIdentifier(LanguageAspect $languageAspect): string
|
|
{
|
|
return sprintf(
|
|
'%d-%s-%s',
|
|
$languageAspect->getContentId(),
|
|
$languageAspect->getOverlayType(),
|
|
implode(',', $languageAspect->getFallbackChain())
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Extract the base identifier (before '@') from a full identity map identifier.
|
|
*/
|
|
public function getBaseIdentifier(string $identifier): string
|
|
{
|
|
$pos = strpos($identifier, '@');
|
|
if ($pos !== false) {
|
|
return substr($identifier, 0, $pos);
|
|
}
|
|
return $identifier;
|
|
}
|
|
}
|