extractSpecialPropertiesFromComputed($rawRecordOrProperties); } else { $this->initFromArray($rawRecordOrProperties); } } public function has(string $id): bool { if (parent::has($id)) { return true; } return array_key_exists($id, $this->specialProperties); } public function get(string $id): mixed { if (parent::has($id)) { return parent::get($id); } if (array_key_exists($id, $this->specialProperties)) { return $this->specialProperties[$id]; } throw new RecordPropertyNotFoundException('Record property "' . $id . '" is not available.', 1725892141); } public function getLanguageId(): int { if ($this->systemProperties?->getLanguage() !== null) { return $this->systemProperties->getLanguage()->getLanguageId(); } return (int)($this->specialProperties['_language'] ?? $this->properties['language_tag'] ?? 0); } public function getPageId(): int { if ($this->systemProperties?->getLanguage() !== null) { $translationParent = $this->systemProperties->getLanguage()->getTranslationParent(); return $translationParent > 0 ? $translationParent : $this->getUid(); } $pageId = isset($this->properties['l10n_parent']) && $this->properties['l10n_parent'] > 0 ? $this->properties['l10n_parent'] : $this->getUid(); return (int)$pageId; } public function getTranslationSource(): ?Page { return $this->specialProperties['_TRANSLATION_SOURCE'] ?? null; } public function getRequestedLanguage(): ?int { return $this->specialProperties['_REQUESTED_OVERLAY_LANGUAGE'] ?? null; } public function toArray(bool $includeSystemProperties = false): array { if ($includeSystemProperties) { // When including system properties, return the full raw record overlaid // with resolved properties and special properties for backward compatibility. $result = $this->rawRecord->toArray(); foreach ($this->properties as $key => $property) { if ($property instanceof RecordPropertyClosure) { $this->properties[$key] = $property->instantiate(); } $result[$key] = $this->properties[$key]; } $result += ['_system' => $this->systemProperties?->toArray() ?? []]; $result += $this->specialProperties; return $result; } return parent::toArray(); } public function offsetExists(mixed $offset): bool { return $this->has((string)$offset); } public function offsetGet(mixed $offset): mixed { return $this->has((string)$offset) ? $this->get((string)$offset) : null; } public function offsetSet(mixed $offset, mixed $value): void { $this->properties[$offset] = $value; } public function offsetUnset(mixed $offset): void { unset($this->properties[$offset]); } private function extractSpecialPropertiesFromComputed(RawRecord $rawRecord): void { $computedProperties = $rawRecord->getComputedProperties(); if ($computedProperties->getLocalizedUid() !== null) { $this->specialProperties['_LOCALIZED_UID'] = $computedProperties->getLocalizedUid(); } if ($computedProperties->getRequestedOverlayLanguageId() !== null) { $this->specialProperties['_REQUESTED_OVERLAY_LANGUAGE'] = $computedProperties->getRequestedOverlayLanguageId(); } if ($computedProperties->getTranslationSource() !== null) { $this->specialProperties['_TRANSLATION_SOURCE'] = $computedProperties->getTranslationSource(); } if ($computedProperties->getVersionedUid() !== null) { $this->specialProperties['_ORIG_uid'] = $computedProperties->getVersionedUid(); } // Extract remaining special properties from the raw record $rawProperties = $rawRecord->toArray(); foreach ($this->specialPropertyNames as $name) { if (isset($rawProperties[$name]) && !isset($this->specialProperties[$name])) { $this->specialProperties[$name] = $rawProperties[$name]; } } } private function initFromArray(array $properties): void { $regularProperties = []; $translationSource = null; $localizedUid = null; $versionedUid = null; $requestedOverlayLanguageId = null; foreach ($properties as $propertyName => $propertyValue) { if (in_array($propertyName, $this->specialPropertyNames)) { if ($propertyName === '_TRANSLATION_SOURCE' && !$propertyValue instanceof Page) { $translationSource = new Page($propertyValue); $this->specialProperties[$propertyName] = $translationSource; } elseif ($propertyName === '_TRANSLATION_SOURCE') { $translationSource = $propertyValue; $this->specialProperties[$propertyName] = $propertyValue; } elseif ($propertyName === '_LOCALIZED_UID') { $localizedUid = $propertyValue; $this->specialProperties[$propertyName] = $propertyValue; } elseif ($propertyName === '_ORIG_uid') { $versionedUid = $propertyValue; $this->specialProperties[$propertyName] = $propertyValue; } elseif ($propertyName === '_REQUESTED_OVERLAY_LANGUAGE') { $requestedOverlayLanguageId = $propertyValue; $this->specialProperties[$propertyName] = $propertyValue; } else { $this->specialProperties[$propertyName] = $propertyValue; } } else { $regularProperties[$propertyName] = $propertyValue; } } $computedProperties = new ComputedProperties( versionedUid: $versionedUid, localizedUid: $localizedUid, requestedOverlayLanguageId: $requestedOverlayLanguageId, translationSource: $translationSource ); $recordType = isset($regularProperties['doktype']) ? (string)$regularProperties['doktype'] : null; $fullType = $recordType !== null ? 'pages.' . $recordType : 'pages'; $rawRecord = new RawRecord( uid: (int)($regularProperties['uid'] ?? 0), pid: (int)($regularProperties['pid'] ?? 0), properties: $regularProperties, computedProperties: $computedProperties, fullType: $fullType ); parent::__construct($rawRecord, $regularProperties, null); } }