> */ 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; } }