diff --git a/php-transformer/src/HtmlToBlocks/Elements/RuntimeIslandContext.php b/php-transformer/src/HtmlToBlocks/Elements/RuntimeIslandContext.php
index bd90f669..f08d8646 100644
--- a/php-transformer/src/HtmlToBlocks/Elements/RuntimeIslandContext.php
+++ b/php-transformer/src/HtmlToBlocks/Elements/RuntimeIslandContext.php
@@ -4,6 +4,7 @@
namespace Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Elements;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Diagnostics\FallbackEmitter;
+use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Session\HtmlTransformerSession;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Session\RuntimeDomState;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Session\RuntimeSelectorState;
use Closure;
@@ -12,17 +13,12 @@
/**
* Explicit collaborator surface for {@see RuntimeIslandAnalyzer}.
*
- * The session state objects and the fallback emitter are resolved lazily
- * through closures because they are per-transform: the analyzer is constructed
- * once with the transformer, but must see the state belonging to the transform
- * currently running.
+ * Per-transform state is read from the compilation's typed session. Closures
+ * remain only for transformer-owned operations.
*/
final class RuntimeIslandContext
{
/**
- * @param Closure(): FallbackEmitter $fallbackEmitter
- * @param Closure(): RuntimeDomState $runtimeDom
- * @param Closure(): RuntimeSelectorState $runtimeSelectors
* @param Closure(DOMElement): iterable $descendantElements
* @param Closure(DOMElement): array> $requiredScriptsForElement
* @param Closure(string): ?DOMElement $preservedHtmlRootElement
@@ -31,9 +27,7 @@ final class RuntimeIslandContext
* @param Closure(string): bool $isPresentationalAnimationSelector
*/
public function __construct(
- private readonly Closure $fallbackEmitter,
- private readonly Closure $runtimeDom,
- private readonly Closure $runtimeSelectors,
+ private readonly HtmlTransformerSession $session,
private readonly Closure $descendantElements,
private readonly Closure $requiredScriptsForElement,
private readonly Closure $preservedHtmlRootElement,
@@ -45,17 +39,17 @@ public function __construct(
public function fallbackEmitter(): FallbackEmitter
{
- return ($this->fallbackEmitter)();
+ return $this->session->fallbackEmitter();
}
public function runtimeDom(): RuntimeDomState
{
- return ($this->runtimeDom)();
+ return $this->session->runtimeDomState();
}
public function runtimeSelectors(): RuntimeSelectorState
{
- return ($this->runtimeSelectors)();
+ return $this->session->runtimeSelectorState();
}
/**
diff --git a/php-transformer/src/HtmlToBlocks/Elements/SearchBlockConversionContext.php b/php-transformer/src/HtmlToBlocks/Elements/SearchBlockConversionContext.php
index 240e3877..cff3ac6f 100644
--- a/php-transformer/src/HtmlToBlocks/Elements/SearchBlockConversionContext.php
+++ b/php-transformer/src/HtmlToBlocks/Elements/SearchBlockConversionContext.php
@@ -3,6 +3,7 @@
namespace Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Elements;
+use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Session\HtmlTransformerSession;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Support\SourceDom;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Classification\FormControlClassifier;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Style\GeneratedSupportStylesheetState;
@@ -17,16 +18,15 @@ final class SearchBlockConversionContext
* @param Closure(DOMElement): array $presentationDeclarations
* @param Closure(string, array, array>, ?DOMElement): array $createBlock
* @param Closure(string): string $restoreSvgCasing
- * @param Closure(): GeneratedSupportStylesheetState $generatedSupportStyles
* @param Closure(DOMElement): bool $isRuntimeDomTarget
* @param Closure(DOMElement): array $htmlPreservationBlock
*/
public function __construct(
+ private readonly HtmlTransformerSession $session,
private readonly Closure $presentationAttributes,
private readonly Closure $presentationDeclarations,
private readonly Closure $createBlock,
private readonly Closure $restoreSvgCasing,
- private readonly Closure $generatedSupportStyles,
private readonly Closure $isRuntimeDomTarget,
private readonly Closure $htmlPreservationBlock
) {
@@ -82,7 +82,7 @@ public function restoreSvgCasing(string $html): string
public function generatedSupportStyles(): GeneratedSupportStylesheetState
{
- return ($this->generatedSupportStyles)();
+ return $this->session->generatedSupportStylesheetState();
}
public function isRuntimeDomTarget(DOMElement $element): bool
diff --git a/php-transformer/src/HtmlToBlocks/HtmlCompilation.php b/php-transformer/src/HtmlToBlocks/HtmlCompilation.php
index 788579b8..a1beb1e2 100644
--- a/php-transformer/src/HtmlToBlocks/HtmlCompilation.php
+++ b/php-transformer/src/HtmlToBlocks/HtmlCompilation.php
@@ -845,18 +845,11 @@ private function onSourceMarkupMutated(): void
$this->sourceStyles()->invalidateSelectorMatches();
}
- /**
- * Collaborator surface for {@see StyleResolver}. Per-transform state is
- * resolved lazily so the resolver always sees the running transform.
- */
+ /** Collaborator surface for {@see StyleResolver}. */
private function createStyleResolutionContext(): StyleResolutionContext
{
return new StyleResolutionContext(
- fn (): AuthorStyleAnalysis => $this->authorStyles(),
- fn (): SourceStyleResolutionState => $this->sourceStyles(),
- fn (): LayoutGeometryState => $this->layoutGeometry(),
- fn (): PresentationResolutionCache => $this->presentationResolutionCache(),
- fn (): TransformationEvidenceState => $this->transformationEvidence(),
+ $this->session,
fn (DOMElement $element): int => $this->cardLikeChildCount($element),
fn (string $value): string => $this->cssComparableValue($value),
fn (string $selector): array => $this->parsedCssSelector($selector),
@@ -906,40 +899,29 @@ private function materializeStylesheetAsset(array $cssParts, string $source, str
));
}
- /**
- * Collaborator surface for {@see NavigationToggleSuppressor}. Per-transform
- * state is resolved lazily so the suppressor always sees the running
- * transform.
- */
+ /** Collaborator surface for {@see NavigationToggleSuppressor}. */
private function createNavigationToggleSuppressionContext(): NavigationToggleSuppressionContext
{
return new NavigationToggleSuppressionContext(
+ $this->session,
fn (DOMElement $element): bool => $this->sourceElementStartsHidden($element),
- fn (): RuntimeSelectorState => $this->runtimeSelectors(),
- fn (): NavigationProjectionState => $this->navigationProjection(),
fn (): PatternRecognizerRegistry => $this->patternRecognizers,
fn (): PatternContext => $this->probePatternContext()
);
}
- /**
- * Collaborator surface for {@see SvgMaterializer}. Per-transform state is
- * resolved lazily so the materializer always sees the running transform.
- */
+ /** Collaborator surface for {@see SvgMaterializer}. */
private function createSvgMaterializationContext(): SvgMaterializationContext
{
return new SvgMaterializationContext(
+ $this->session,
fn (string $name, array $attrs = array(), array $innerBlocks = array(), ?DOMElement $sourceElement = null, ?DOMElement $logicalSourceElement = null): array
=> $this->createBlock($name, $attrs, $innerBlocks, $sourceElement, $logicalSourceElement),
fn (string $tagName): bool => $this->sourceElementClassifier->isInlineContentElement($tagName),
fn (DOMElement $element): bool => $this->sourceElementClassifier->isVisualLayerElement($element),
- fn (): LayoutGeometryState => $this->layoutGeometry(),
- fn (): AssetMaterializationState => $this->materializedAssets(),
fn (DOMElement $element): ?string => $this->reusableComponentFingerprintFor($element),
fn (DOMElement $element): string => $this->safeFallbackHtml($element),
- fn (DOMElement $element): string => $this->sanitizeInlineSvgMarkup($element),
- fn (): TransformationEvidenceState => $this->transformationEvidence(),
- fn (): TransformationProvenanceState => $this->transformationProvenance()
+ fn (DOMElement $element): string => $this->sanitizeInlineSvgMarkup($element)
);
}
@@ -947,30 +929,21 @@ private function createSvgMaterializationContext(): SvgMaterializationContext
private function createSearchBlockConversionContext(): SearchBlockConversionContext
{
return new SearchBlockConversionContext(
+ $this->session,
fn (DOMElement $element): array => $this->styleResolver->presentationAttributes($element),
fn (DOMElement $element): array => $this->styleResolver->presentationDeclarations($element),
fn (string $name, array $attributes, array $innerBlocks, ?DOMElement $sourceElement): array => $this->createBlock($name, $attributes, $innerBlocks, $sourceElement),
fn (string $html): string => $this->svgMaterializer->restoreSvgCasing($html),
- fn (): GeneratedSupportStylesheetState => $this->generatedSupportStyles(),
fn (DOMElement $element): bool => $this->runtimeIslands->isRuntimeDomTarget($element),
fn (DOMElement $element): array => $this->htmlPreservationBlock($element)
);
}
- /**
- * Collaborator surface for {@see NavigationStyleProjector}. Per-transform
- * state is resolved lazily so the projector always sees the running
- * transform.
- */
+ /** Collaborator surface for {@see NavigationStyleProjector}. */
private function createNavigationStyleProjectionContext(): NavigationStyleProjectionContext
{
return new NavigationStyleProjectionContext(
- fn (): AuthorStyleAnalysis => $this->authorStyles(),
- fn (): SourceStyleResolutionState => $this->sourceStyles(),
- fn (): AuthorSelectorProjectionState => $this->session->authorSelectorProjectionState(),
- fn (): GeneratedSupportStylesheetState => $this->generatedSupportStyles(),
- fn (): RuntimeBehaviorState => $this->runtimeBehavior(),
- fn (): TransformationEvidenceState => $this->transformationEvidence(),
+ $this->session,
fn (string $selector): array => $this->parsedCssSelector($selector),
function (array $cssParts, string $source, string $placement, string $pathPrefix, string $target = 'both'): void {
$this->materializeStylesheetAsset($cssParts, $source, $placement, $pathPrefix, $target);
@@ -1007,16 +980,11 @@ function (DOMElement $element, array &$fallbacks): ?array {
);
}
- /**
- * Collaborator surface for {@see RuntimeIslandAnalyzer}. Session-scoped
- * state is resolved lazily so the analyzer always sees the running transform.
- */
+ /** Collaborator surface for {@see RuntimeIslandAnalyzer}. */
private function createRuntimeIslandContext(): RuntimeIslandContext
{
return new RuntimeIslandContext(
- fn (): FallbackEmitter => $this->fallbackEmitter(),
- fn (): RuntimeDomState => $this->runtimeDom(),
- fn (): RuntimeSelectorState => $this->runtimeSelectors(),
+ $this->session,
fn (DOMElement $element): iterable => $this->descendantElements($element),
fn (DOMElement $element): array => $this->requiredScriptsForElement($element),
fn (string $html): ?DOMElement => $this->preservedHtmlRootElement($html),
@@ -1111,14 +1079,12 @@ function (DOMElement $element, array &$fallbacks, bool $captureUnsupported): arr
private function authorStyles(): AuthorStyleAnalysis
{
- return $this->session->authorStyleAnalysis()
- ?? throw new \LogicException('Author styles have not been prepared for this transform.');
+ return $this->session->authorStyleAnalysis();
}
private function layoutGeometry(): LayoutGeometryState
{
- return $this->session->layoutGeometryState()
- ?? throw new \LogicException('Layout geometry state has not been prepared for this transform.');
+ return $this->session->layoutGeometryState();
}
private function transformationProvenance(): TransformationProvenanceState
@@ -1154,8 +1120,7 @@ private function generatedBlocks(): GeneratedBlockRegistry
private function materializedAssets(): AssetMaterializationState
{
- return $this->session->assetMaterializationState()
- ?? throw new \LogicException('Asset materialization state has not been prepared for this transform.');
+ return $this->session->assetMaterializationState();
}
private function runtimeDom(): RuntimeDomState
diff --git a/php-transformer/src/HtmlToBlocks/Session/HtmlTransformerSession.php b/php-transformer/src/HtmlToBlocks/Session/HtmlTransformerSession.php
index c8204f17..2e5b13b1 100644
--- a/php-transformer/src/HtmlToBlocks/Session/HtmlTransformerSession.php
+++ b/php-transformer/src/HtmlToBlocks/Session/HtmlTransformerSession.php
@@ -65,9 +65,10 @@ public function installAuthorStyleAnalysis(AuthorStyleAnalysis $analysis): void
$this->authorSelectorProjectionState->installAuthorStyles($analysis);
}
- public function authorStyleAnalysis(): ?AuthorStyleAnalysis
+ public function authorStyleAnalysis(): AuthorStyleAnalysis
{
- return $this->authorStyleAnalysis;
+ return $this->authorStyleAnalysis
+ ?? throw new \LogicException('Author styles have not been prepared for this transform.');
}
public function installLayoutGeometryState(LayoutGeometryState $state): void
@@ -75,9 +76,10 @@ public function installLayoutGeometryState(LayoutGeometryState $state): void
$this->layoutGeometryState = $state;
}
- public function layoutGeometryState(): ?LayoutGeometryState
+ public function layoutGeometryState(): LayoutGeometryState
{
- return $this->layoutGeometryState;
+ return $this->layoutGeometryState
+ ?? throw new \LogicException('Layout geometry state has not been prepared for this transform.');
}
public function installGeneratedBlockRegistry(GeneratedBlockRegistry $registry): void
@@ -95,9 +97,10 @@ public function installAssetMaterializationState(AssetMaterializationState $stat
$this->assetMaterializationState = $state;
}
- public function assetMaterializationState(): ?AssetMaterializationState
+ public function assetMaterializationState(): AssetMaterializationState
{
- return $this->assetMaterializationState;
+ return $this->assetMaterializationState
+ ?? throw new \LogicException('Asset materialization state has not been prepared for this transform.');
}
public function runtimeDomState(): RuntimeDomState
diff --git a/php-transformer/src/HtmlToBlocks/Style/NavigationStyleProjectionContext.php b/php-transformer/src/HtmlToBlocks/Style/NavigationStyleProjectionContext.php
index f9351f8b..134c3cc5 100644
--- a/php-transformer/src/HtmlToBlocks/Style/NavigationStyleProjectionContext.php
+++ b/php-transformer/src/HtmlToBlocks/Style/NavigationStyleProjectionContext.php
@@ -3,6 +3,7 @@
namespace Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Style;
+use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Session\HtmlTransformerSession;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Session\RuntimeBehaviorState;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Session\TransformationEvidenceState;
use Closure;
@@ -11,11 +12,8 @@
/**
* Explicit collaborator surface for {@see NavigationStyleProjector}.
*
- * Per-transform state (author styles, source styles, generated support styles,
- * materialized assets, runtime behavior, transformation evidence) is resolved
- * through closures because the projector is constructed once with the
- * transformer but must see the state belonging to the transform currently
- * running.
+ * Per-transform state is read from the compilation's typed session. Closures
+ * remain only for transformer-owned operations.
*
* `materializeStylesheetAsset` is a transformer-owned operation rather than a
* navigation concern — the transformer uses it for engine-support and author
@@ -25,22 +23,11 @@
final class NavigationStyleProjectionContext
{
/**
- * @param Closure(): AuthorStyleAnalysis $authorStyles
- * @param Closure(): SourceStyleResolutionState $sourceStyles
- * @param Closure(): AuthorSelectorProjectionState $selectorProjections
- * @param Closure(): GeneratedSupportStylesheetState $generatedSupportStyles
- * @param Closure(): RuntimeBehaviorState $runtimeBehavior
- * @param Closure(): TransformationEvidenceState $transformationEvidence
* @param Closure(string): array $parsedCssSelector
* @param Closure(array, string, string, string, string): void $materializeStylesheetAsset
*/
public function __construct(
- private readonly Closure $authorStyles,
- private readonly Closure $sourceStyles,
- private readonly Closure $selectorProjections,
- private readonly Closure $generatedSupportStyles,
- private readonly Closure $runtimeBehavior,
- private readonly Closure $transformationEvidence,
+ private readonly HtmlTransformerSession $session,
private readonly Closure $parsedCssSelector,
private readonly Closure $materializeStylesheetAsset
) {
@@ -48,32 +35,32 @@ public function __construct(
public function authorStyles(): AuthorStyleAnalysis
{
- return ($this->authorStyles)();
+ return $this->session->authorStyleAnalysis();
}
public function sourceStyles(): SourceStyleResolutionState
{
- return ($this->sourceStyles)();
+ return $this->session->sourceStyleResolutionState();
}
public function selectorProjections(): AuthorSelectorProjectionState
{
- return ($this->selectorProjections)();
+ return $this->session->authorSelectorProjectionState();
}
public function generatedSupportStyles(): GeneratedSupportStylesheetState
{
- return ($this->generatedSupportStyles)();
+ return $this->session->generatedSupportStylesheetState();
}
public function runtimeBehavior(): RuntimeBehaviorState
{
- return ($this->runtimeBehavior)();
+ return $this->session->runtimeBehaviorState();
}
public function transformationEvidence(): TransformationEvidenceState
{
- return ($this->transformationEvidence)();
+ return $this->session->transformationEvidenceState();
}
/**
diff --git a/php-transformer/src/HtmlToBlocks/Style/StyleResolutionContext.php b/php-transformer/src/HtmlToBlocks/Style/StyleResolutionContext.php
index f1b74da0..5b6acd89 100644
--- a/php-transformer/src/HtmlToBlocks/Style/StyleResolutionContext.php
+++ b/php-transformer/src/HtmlToBlocks/Style/StyleResolutionContext.php
@@ -3,6 +3,7 @@
namespace Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Style;
+use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Session\HtmlTransformerSession;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Session\TransformationEvidenceState;
use Closure;
use DOMElement;
@@ -10,19 +11,12 @@
/**
* Explicit collaborator surface for {@see StyleResolver}.
*
- * Per-transform state (author styles, source styles, layout geometry, the
- * presentation cache, transformation evidence) is resolved through closures
- * because the resolver is constructed once with the transformer but must see
- * the state belonging to the transform currently running.
+ * Per-transform state is read from the compilation's typed session. Closures
+ * remain only for transformer-owned operations.
*/
final class StyleResolutionContext
{
/**
- * @param Closure(): AuthorStyleAnalysis $authorStyles
- * @param Closure(): SourceStyleResolutionState $sourceStyles
- * @param Closure(): LayoutGeometryState $layoutGeometry
- * @param Closure(): PresentationResolutionCache $presentationResolutionCache
- * @param Closure(): TransformationEvidenceState $transformationEvidence
* @param Closure(DOMElement): int $cardLikeChildCount
* @param Closure(string): string $cssComparableValue
* @param Closure(string): array $parsedCssSelector
@@ -31,11 +25,7 @@ final class StyleResolutionContext
* @param Closure(DOMElement): bool $hasRetainedPresentationRuntime
*/
public function __construct(
- private readonly Closure $authorStyles,
- private readonly Closure $sourceStyles,
- private readonly Closure $layoutGeometry,
- private readonly Closure $presentationResolutionCache,
- private readonly Closure $transformationEvidence,
+ private readonly HtmlTransformerSession $session,
private readonly Closure $cardLikeChildCount,
private readonly Closure $cssComparableValue,
private readonly Closure $parsedCssSelector,
@@ -47,27 +37,27 @@ public function __construct(
public function authorStyles(): AuthorStyleAnalysis
{
- return ($this->authorStyles)();
+ return $this->session->authorStyleAnalysis();
}
public function sourceStyles(): SourceStyleResolutionState
{
- return ($this->sourceStyles)();
+ return $this->session->sourceStyleResolutionState();
}
public function layoutGeometry(): LayoutGeometryState
{
- return ($this->layoutGeometry)();
+ return $this->session->layoutGeometryState();
}
public function presentationResolutionCache(): PresentationResolutionCache
{
- return ($this->presentationResolutionCache)();
+ return $this->session->presentationResolutionCache();
}
public function transformationEvidence(): TransformationEvidenceState
{
- return ($this->transformationEvidence)();
+ return $this->session->transformationEvidenceState();
}
public function cardLikeChildCount(DOMElement $element): int
diff --git a/php-transformer/src/HtmlToBlocks/Support/NavigationToggleSuppressionContext.php b/php-transformer/src/HtmlToBlocks/Support/NavigationToggleSuppressionContext.php
index be638fc8..3f053eaa 100644
--- a/php-transformer/src/HtmlToBlocks/Support/NavigationToggleSuppressionContext.php
+++ b/php-transformer/src/HtmlToBlocks/Support/NavigationToggleSuppressionContext.php
@@ -5,6 +5,7 @@
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Patterns\PatternContext;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Patterns\PatternRecognizerRegistry;
+use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Session\HtmlTransformerSession;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Session\NavigationProjectionState;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Session\RuntimeSelectorState;
use Closure;
@@ -13,24 +14,19 @@
/**
* Explicit collaborator surface for {@see NavigationToggleSuppressor}.
*
- * Per-transform state (runtime selectors, navigation projection) and the probe
- * pattern context are resolved through closures because the suppressor is constructed once with the
- * transformer but must see the state belonging to the transform currently
- * running.
+ * Per-transform state is read from the compilation's typed session. Closures
+ * remain only for transformer-owned operations.
*/
final class NavigationToggleSuppressionContext
{
/**
* @param Closure(DOMElement): bool $sourceElementStartsHidden
- * @param Closure(): RuntimeSelectorState $runtimeSelectors
- * @param Closure(): NavigationProjectionState $navigationProjection
* @param Closure(): PatternRecognizerRegistry $patternRecognizers
* @param Closure(): PatternContext $probePatternContext
*/
public function __construct(
+ private readonly HtmlTransformerSession $session,
private readonly Closure $sourceElementStartsHidden,
- private readonly Closure $runtimeSelectors,
- private readonly Closure $navigationProjection,
private readonly Closure $patternRecognizers,
private readonly Closure $probePatternContext
) {
@@ -43,12 +39,12 @@ public function sourceElementStartsHidden(DOMElement $element): bool
public function runtimeSelectors(): RuntimeSelectorState
{
- return ($this->runtimeSelectors)();
+ return $this->session->runtimeSelectorState();
}
public function navigationProjection(): NavigationProjectionState
{
- return ($this->navigationProjection)();
+ return $this->session->navigationProjectionState();
}
public function patternRecognizers(): PatternRecognizerRegistry
diff --git a/php-transformer/src/HtmlToBlocks/Support/SvgMaterializationContext.php b/php-transformer/src/HtmlToBlocks/Support/SvgMaterializationContext.php
index 0cf254d3..2d8cbd4f 100644
--- a/php-transformer/src/HtmlToBlocks/Support/SvgMaterializationContext.php
+++ b/php-transformer/src/HtmlToBlocks/Support/SvgMaterializationContext.php
@@ -4,6 +4,7 @@
namespace Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Support;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Session\AssetMaterializationState;
+use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Session\HtmlTransformerSession;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Session\TransformationEvidenceState;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Session\TransformationProvenanceState;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Style\LayoutGeometryState;
@@ -13,10 +14,8 @@
/**
* Explicit collaborator surface for {@see SvgMaterializer}.
*
- * Per-transform state (layout geometry, materialized assets, transformation
- * evidence and provenance) is resolved through closures because the materializer
- * is constructed once with the transformer but must see the state belonging to
- * the transform currently running.
+ * Per-transform state is read from the compilation's typed session. Closures
+ * remain only for transformer-owned operations.
*/
final class SvgMaterializationContext
{
@@ -24,25 +23,18 @@ final class SvgMaterializationContext
* @param Closure(string, array, array>, ?DOMElement, ?DOMElement): array $createBlock
* @param Closure(string): bool $isInlineContentElement
* @param Closure(DOMElement): bool $isVisualLayerElement
- * @param Closure(): LayoutGeometryState $layoutGeometry
- * @param Closure(): AssetMaterializationState $materializedAssets
* @param Closure(DOMElement): ?string $reusableComponentFingerprintFor
* @param Closure(DOMElement): string $safeFallbackHtml
* @param Closure(DOMElement): string $sanitizeInlineSvgMarkup
- * @param Closure(): TransformationEvidenceState $transformationEvidence
- * @param Closure(): TransformationProvenanceState $transformationProvenance
*/
public function __construct(
+ private readonly HtmlTransformerSession $session,
private readonly Closure $createBlock,
private readonly Closure $isInlineContentElement,
private readonly Closure $isVisualLayerElement,
- private readonly Closure $layoutGeometry,
- private readonly Closure $materializedAssets,
private readonly Closure $reusableComponentFingerprintFor,
private readonly Closure $safeFallbackHtml,
- private readonly Closure $sanitizeInlineSvgMarkup,
- private readonly Closure $transformationEvidence,
- private readonly Closure $transformationProvenance
+ private readonly Closure $sanitizeInlineSvgMarkup
) {
}
@@ -73,12 +65,12 @@ public function isVisualLayerElement(DOMElement $element): bool
public function layoutGeometry(): LayoutGeometryState
{
- return ($this->layoutGeometry)();
+ return $this->session->layoutGeometryState();
}
public function materializedAssets(): AssetMaterializationState
{
- return ($this->materializedAssets)();
+ return $this->session->assetMaterializationState();
}
public function reusableComponentFingerprintFor(DOMElement $element): ?string
@@ -98,11 +90,11 @@ public function sanitizeInlineSvgMarkup(DOMElement $element): string
public function transformationEvidence(): TransformationEvidenceState
{
- return ($this->transformationEvidence)();
+ return $this->session->transformationEvidenceState();
}
public function transformationProvenance(): TransformationProvenanceState
{
- return ($this->transformationProvenance)();
+ return $this->session->transformationProvenanceState();
}
}
diff --git a/php-transformer/tests/unit/author-selector-semantics.php b/php-transformer/tests/unit/author-selector-semantics.php
index 810f3d57..8a5a6b4f 100644
--- a/php-transformer/tests/unit/author-selector-semantics.php
+++ b/php-transformer/tests/unit/author-selector-semantics.php
@@ -620,14 +620,14 @@
$applicabilityCompilation = new HtmlCompilation();
$applicabilityCompilation->transform('Present
');
$applicabilitySession = (new ReflectionClass($applicabilityCompilation))->getProperty('session')->getValue($applicabilityCompilation);
-$applicableRules = $applicabilitySession->authorStyleAnalysis()?->styleRules() ?? array();
+$applicableRules = $applicabilitySession->authorStyleAnalysis()->styleRules();
$applicableSelectors = array_column(array_merge(...array_column($applicableRules, 'selectors')), 'selector');
$assert(array('.present') === $applicableSelectors, 'the installed page-matching graph omits selectors whose required source signals are absent');
$unmatchableCompilation = new HtmlCompilation();
$unmatchableCompilation->transform('');
$unmatchableSession = (new ReflectionClass($unmatchableCompilation))->getProperty('session')->getValue($unmatchableCompilation);
-$unmatchableIndex = $unmatchableSession->authorStyleAnalysis()?->styleRuleCandidateIndex() ?? array();
+$unmatchableIndex = $unmatchableSession->authorStyleAnalysis()->styleRuleCandidateIndex();
$indexedAuthorSelectors = array();
foreach ( array( 'universal', 'ids', 'classes', 'tags', 'attributes' ) as $bucket ) {
$entries = 'universal' === $bucket ? $unmatchableIndex[$bucket] : array_merge(...array_values($unmatchableIndex[$bucket] ?: array(array())));
diff --git a/php-transformer/tests/unit/html-transformer-session-state.php b/php-transformer/tests/unit/html-transformer-session-state.php
index 2bfcf84a..c9a22fce 100644
--- a/php-transformer/tests/unit/html-transformer-session-state.php
+++ b/php-transformer/tests/unit/html-transformer-session-state.php
@@ -5,6 +5,7 @@
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\HtmlTransformer;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Session\HtmlTransformerSession;
+use Automattic\BlocksEngine\PhpTransformer\WordPress\Runtime;
$assert = static function (bool $condition, string $message): void {
if ( ! $condition ) {
@@ -108,4 +109,18 @@
$assert(! $transformerReflection->hasMethod($magicAccessor), 'HtmlTransformer must not delegate state through ' . $magicAccessor . '.');
}
+$unpreparedSession = new HtmlTransformerSession(new Runtime(), static fn (DOMElement $element): array => array());
+foreach ( array(
+ 'authorStyleAnalysis' => 'Author styles have not been prepared for this transform.',
+ 'layoutGeometryState' => 'Layout geometry state has not been prepared for this transform.',
+ 'assetMaterializationState' => 'Asset materialization state has not been prepared for this transform.',
+) as $method => $message ) {
+ try {
+ $unpreparedSession->{$method}();
+ $assert(false, $method . ' must fail before its lifecycle state is installed.');
+ } catch ( LogicException $exception ) {
+ $assert($message === $exception->getMessage(), $method . ' must preserve its lifecycle failure.');
+ }
+}
+
fwrite(STDOUT, "HTML transformer session state passed\n");
diff --git a/php-transformer/tests/unit/runtime-island-analyzer.php b/php-transformer/tests/unit/runtime-island-analyzer.php
index b344d202..008629e1 100644
--- a/php-transformer/tests/unit/runtime-island-analyzer.php
+++ b/php-transformer/tests/unit/runtime-island-analyzer.php
@@ -17,8 +17,10 @@
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Elements\PseudoFormAnalyzer;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Elements\RuntimeIslandAnalyzer;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Elements\RuntimeIslandContext;
+use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Session\HtmlTransformerSession;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Session\RuntimeDomState;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Session\RuntimeSelectorState;
+use Automattic\BlocksEngine\PhpTransformer\WordPress\Runtime;
$assertions = 0;
$failures = array();
@@ -71,9 +73,6 @@
);
$defaults = array(
- 'fallbackEmitter' => static fn () => throw new RuntimeException('fallbackEmitter not expected in this test'),
- 'runtimeDom' => static fn (): RuntimeDomState => new RuntimeDomState(),
- 'runtimeSelectors' => static fn (): RuntimeSelectorState => $selectors,
'attr' => static fn (DOMElement $e, string $n): string => $e->getAttribute($n),
'descendants' => $descendants,
'islandSelector' => static fn (DOMElement $e): string => strtolower($e->tagName),
@@ -86,13 +85,13 @@
'dedupe' => static fn (array $rows): array => array_values($rows),
);
$c = array_merge($defaults, $overrides);
+ $session = $c['session'] ?? new HtmlTransformerSession(new Runtime(), static fn (DOMElement $element): array => array());
+ $session->installRuntimeSelectorState($selectors);
$metadataBuilder = new FormControlMetadataBuilder($c['islandSelector']);
$pseudoFormAnalyzer = new PseudoFormAnalyzer($metadataBuilder, $c['islandSelector']);
return new RuntimeIslandAnalyzer(new RuntimeIslandContext(
- $c['fallbackEmitter'],
- $c['runtimeDom'],
- $c['runtimeSelectors'],
+ $session,
$c['descendants'],
$c['requiredScripts'],
$c['preservedRoot'],
@@ -168,9 +167,10 @@
$assert(! $retain->canRetainRuntimeDomContractNatively($elementFrom(''), 'core/image'), 'unsupported-block-name-does-not-retain');
// Block materialization delegates the complete runtime DOM recording decision.
-$runtimeDom = new RuntimeDomState();
+$blockRuntimeSession = new HtmlTransformerSession(new Runtime(), static fn (DOMElement $element): array => array());
+$runtimeDom = $blockRuntimeSession->runtimeDomState();
$blockRuntime = $makeAnalyzer(array('#mount'), array(), array(
- 'runtimeDom' => static fn (): RuntimeDomState => $runtimeDom,
+ 'session' => $blockRuntimeSession,
));
$assert($blockRuntime->recordBlockRuntimeDomContract($elementFrom(''), 'core/group'), 'runtime-block-contract-recorded');
$assert(array(array('block_name' => 'core/group', 'tag' => 'section', 'selector' => '#mount')) === $runtimeDom->preservations(), 'runtime-block-native-preservation-recorded');