Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion php-transformer/src/ArtifactCompiler/ArtifactCompiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ public function compile(array $artifact): TransformerResult
$blockTypes = $this->detectBlockTypes($normalized['files'], $diagnostics);
$companionPluginPayloadBuilder = new CompanionPluginPayload();
$entryBlocks = $this->compileEntryBlocks($html, $entryPath, $normalized['files'], $companionPluginPayloadBuilder->blockNamespace($artifact));
$assets = array_merge($this->assetManifest($normalized['files'], $entryPath, $referenceReports['asset_references']), $entryBlocks['assets']);
$manifestAssets = $this->assetManifest($normalized['files'], $entryPath, $referenceReports['asset_references']);
$geometryAssets = array_values(array_filter($entryBlocks['assets'], static fn (array $asset): bool => 'css' === ($asset['kind'] ?? '') && str_contains((string) ($asset['content'] ?? ''), '.be-inline-geometry-')));
$otherGeneratedAssets = array_values(array_filter($entryBlocks['assets'], static fn (array $asset): bool => ! in_array($asset, $geometryAssets, true)));
// Runtime loads the manifest in array order. Put carrier CSS before
// authored assets so authored !important declarations preserve cascade.
$assets = array_merge($geometryAssets, $manifestAssets, $otherGeneratedAssets);
$diagnostics = array_merge($diagnostics, $entryBlocks['diagnostics']);
$serializedBlocks = $entryBlocks['serialized_blocks'];
if ( '' === $serializedBlocks && ! empty($documents['documents'][0]['block_markup']) ) {
Expand Down
5 changes: 4 additions & 1 deletion php-transformer/src/HtmlToBlocks/BlockFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ private function normalizeClassNameAttr(array $attrs): array
*/
private function commentAttrs(string $name, array $attrs): array
{
unset($attrs['inlineGeometryStyle']);
if ( 'core/paragraph' === $name && preg_match('/^\s*<a\b/i', (string) ($attrs['content'] ?? '')) ) {
unset($attrs['content']);
}
Expand Down Expand Up @@ -346,6 +347,7 @@ private function buttonHtml(array $attrs): string
$wrapperAttrs = array(
'id' => (string) ($attrs['anchor'] ?? ''),
'class' => $this->mergeClassNames('wp-block-button', $this->buttonWidthClasses($attrs), (string) ($attrs['className'] ?? '')),
'style' => (string) ($attrs['inlineGeometryStyle'] ?? ''),
);

$controlAttrs = array(
Expand Down Expand Up @@ -650,10 +652,11 @@ private function blockSupportAttrs(array $attrs, string $baseClass = ''): string
$presetClasses = $this->presetColorClasses($attrs);
$layoutClasses = $this->layoutClasses($attrs['layout'] ?? null, $baseClass);
$classes = $this->mergeClassNames($baseClass, $presetClasses, $support['classes'], $layoutClasses, (string) ($attrs['className'] ?? ''));
$style = trim((string) $support['style'] . ';' . (string) ($attrs['inlineGeometryStyle'] ?? ''), ';');
return $this->htmlAttrs(array(
'id' => (string) ($attrs['anchor'] ?? ''),
'class' => $classes,
'style' => $support['style'],
'style' => $style,
));
}

Expand Down
36 changes: 24 additions & 12 deletions php-transformer/src/HtmlToBlocks/HtmlTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -471,9 +471,12 @@ public function transform(string $html, array $options = array()): TransformerRe
$this->appendCommerceControlsFallbacks($body, $fallbacks);
$sourceProvenance = $this->sourceProvenanceForBlocks($blocks);
$serializedBlocks = $this->runtime->serializeBlocks($blocks);
if ( true !== ($options['skip_author_stylesheet_materialization'] ?? false) ) {
$this->materializeAuthorStylesheet($html, (string) ($options['static_css'] ?? ''));
}
$this->materializeAuthorStylesheet(
$html,
(string) ($options['static_css'] ?? ''),
true !== ($options['skip_author_stylesheet_materialization'] ?? false),
$serializedBlocks
);
$blockValidityReport = $this->runtime->validateBlockSerialization($blocks);
$semanticParityReport = $this->semanticParityReporter->report($body, $blocks, $sourceProvenance, $html, (string) ($options['static_css'] ?? ''));
$contentRoundTripReport = $this->contentRoundTripReporter->report($serializedBlocks, $html, $this->formControlEchoTexts);
Expand Down Expand Up @@ -554,10 +557,17 @@ private function metrics(string $input, array $blocks, string $output, array $fa
);
}

private function materializeAuthorStylesheet(string $html, string $staticCss): void
private function materializeAuthorStylesheet(string $html, string $staticCss, bool $includeAuthorStyles = true, string $serializedBlocks = ''): void
{
$cssParts = array();
if ( preg_match_all('@<style\b[^>]*>(.*?)</style>@is', $html, $matches) ) {
$geometryCss = $this->generatedGeometryCss($serializedBlocks);
if ( '' !== $geometryCss ) {
// Important carrier rules precede author CSS: they retain inline
// precedence over normal selectors while authored !important rules
// remain able to override them.
$cssParts[] = $geometryCss;
}
if ( $includeAuthorStyles && preg_match_all('@<style\b[^>]*>(.*?)</style>@is', $html, $matches) ) {
foreach ( $matches[1] as $styleBlock ) {
$styleBlock = trim(html_entity_decode((string) $styleBlock, ENT_QUOTES | ENT_HTML5, 'UTF-8'));
if ( '' !== $styleBlock ) {
Expand All @@ -566,9 +576,11 @@ private function materializeAuthorStylesheet(string $html, string $staticCss): v
}
}

$staticCss = trim($staticCss);
if ( '' !== $staticCss ) {
$cssParts[] = $staticCss;
if ( $includeAuthorStyles ) {
$staticCss = trim($staticCss);
if ( '' !== $staticCss ) {
$cssParts[] = $staticCss;
}
}

$css = trim(implode("\n\n", $cssParts));
Expand Down Expand Up @@ -823,7 +835,7 @@ private function convertChildren(DOMNode $parent, array &$fallbacks, bool $captu
private function patternContext(bool $includeRuntimeDomTarget = true): PatternContext
{
return new PatternContext(
fn (DOMElement $sourceElement): array => $this->presentationAttributes($sourceElement),
fn (DOMElement $sourceElement, array $excludedGeometryProperties = array()): array => $this->presentationAttributes($sourceElement, $excludedGeometryProperties),
fn (DOMElement $sourceElement): string => $this->innerHtml($sourceElement),
fn (string $name, array $attrs = array(), array $innerBlocks = array(), ?DOMElement $sourceElement = null): array => $this->createBlock($name, $attrs, $innerBlocks, $sourceElement),
$includeRuntimeDomTarget ? fn (DOMElement $sourceElement): bool => $this->isRuntimeDomTarget($sourceElement) : null,
Expand Down Expand Up @@ -859,7 +871,7 @@ private function convertPatternChildrenWithoutTags(DOMElement $element, array $e
private function probePatternContext(): PatternContext
{
return new PatternContext(
fn (DOMElement $sourceElement): array => $this->presentationAttributes($sourceElement),
fn (DOMElement $sourceElement, array $excludedGeometryProperties = array()): array => $this->presentationAttributes($sourceElement, $excludedGeometryProperties),
fn (DOMElement $sourceElement): string => $this->innerHtml($sourceElement),
static fn (string $name, array $attrs = array(), array $innerBlocks = array(), ?DOMElement $sourceElement = null): array => array(
'blockName' => $name,
Expand Down Expand Up @@ -1065,7 +1077,7 @@ private function convertElement(DOMElement $element, array &$fallbacks, bool $ca
fn (DOMElement $sourceElement): string => $this->citationFromElement($sourceElement),
fn (DOMElement $sourceElement, array $excludedTags): string => $this->innerHtmlWithoutTags($sourceElement, $excludedTags),
fn (string $html): string => $this->runtime->stripAllTags($html),
fn (DOMElement $sourceElement): array => $this->presentationAttributes($sourceElement),
fn (DOMElement $sourceElement, array $excludedGeometryProperties = array()): array => $this->presentationAttributes($sourceElement, $excludedGeometryProperties),
fn (DOMElement $sourceElement, array &$sourceFallbacks, array $excludedTags): array => $this->convertChildrenWithoutTags($sourceElement, $sourceFallbacks, $excludedTags),
fn (string $inlineTagName): bool => $this->isInlineContentElement($inlineTagName),
fn (string $name, array $attrs = array(), array $innerBlocks = array(), ?DOMElement $sourceElement = null): array => $this->createBlock($name, $attrs, $innerBlocks, $sourceElement)
Expand Down Expand Up @@ -1379,7 +1391,7 @@ private function convertElement(DOMElement $element, array &$fallbacks, bool $ca
fn (DOMElement $sourceElement): int => $this->childElementCount($sourceElement),
fn (DOMElement $sourceElement, string $name): string => $this->attr($sourceElement, $name),
fn (DOMElement $sourceElement, string $className): bool => $this->hasClass($sourceElement, $className),
fn (DOMElement $sourceElement): array => $this->presentationAttributes($sourceElement),
fn (DOMElement $sourceElement, array $excludedGeometryProperties = array()): array => $this->presentationAttributes($sourceElement, $excludedGeometryProperties),
fn (string $name, array $attrs = array(), array $innerBlocks = array(), ?DOMElement $sourceElement = null): array => $this->createBlock($name, $attrs, $innerBlocks, $sourceElement)
);
if ( null !== $spacer ) {
Expand Down
10 changes: 6 additions & 4 deletions php-transformer/src/HtmlToBlocks/Patterns/ButtonsPattern.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function __construct()

/**
* @param callable(DOMElement): array<string, mixed>|null $fileBlockFromAnchor
* @param callable(DOMElement): array<string, mixed> $presentationAttributes
* @param callable(DOMElement, array<int, string>): array<string, mixed> $presentationAttributes
* @param callable(DOMElement): string $resolvedStyle
* @param callable(DOMElement): string $innerHtml
* @param callable(DOMElement, string): string $attr
Expand Down Expand Up @@ -202,7 +202,11 @@ private function spanWrapsEntireContent(string $inner): bool
*/
private function buttonPresentationAttributes(DOMElement $element, callable $presentationAttributes, callable $resolvedStyle): array
{
$attrs = $presentationAttributes($element);
$resolvedStyle = (string) $resolvedStyle($element);
$width = $this->buttonWidth($resolvedStyle);
// Native core/button width owns only its canonical percentage values.
// Other anchors and width values retain their generated geometry carrier.
$attrs = $presentationAttributes($element, null !== $width ? array( 'width' ) : array());
// Buttons resolve styling from the raw merged CSS string, not the canonical
// block style object, so the (now object-shaped) presentation `style` is
// dropped and re-derived via ButtonStyleResolver below.
Expand All @@ -211,7 +215,6 @@ private function buttonPresentationAttributes(DOMElement $element, callable $pre
// belongs on the parent core/buttons, not each button). Emitting it here
// produces an unsupported attribute and invalid block markup, so drop it.
unset($attrs['layout']);
$resolvedStyle = (string) $resolvedStyle($element);
$isOutline = $this->hasOutlineSignal($element, $resolvedStyle);
if ( $isOutline ) {
$attrs['className'] = $this->mergeClassNames((string) ($attrs['className'] ?? ''), 'is-style-outline');
Expand All @@ -227,7 +230,6 @@ private function buttonPresentationAttributes(DOMElement $element, callable $pre
$attrs = array_merge($attrs, $native);
}

$width = $this->buttonWidth($resolvedStyle);
if ( null !== $width ) {
$attrs['width'] = $width;
}
Expand Down
6 changes: 4 additions & 2 deletions php-transformer/src/HtmlToBlocks/Patterns/SpacerPattern.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final class SpacerPattern
* @param callable(DOMElement): int $childElementCount
* @param callable(DOMElement, string): string $attr
* @param callable(DOMElement, string): bool $hasClass
* @param callable(DOMElement): array<string, mixed> $presentationAttributes
* @param callable(DOMElement, array<int, string>): array<string, mixed> $presentationAttributes
* @param callable(string, array<string, mixed>, array<int, array<string, mixed>>, DOMElement|null): array<string, mixed> $createBlock
* @return array<string, mixed>|null
*/
Expand All @@ -30,7 +30,9 @@ public function match(DOMElement $element, callable $childElementCount, callable
return null;
}

$attrs = $presentationAttributes($element);
// core/spacer serializes height itself. Preserve all remaining geometry
// through the generated stylesheet rather than removing the whole carrier.
$attrs = $presentationAttributes($element, array( 'height' ));
$attrs['height'] = $height;
unset($attrs['style']);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);

namespace Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Style;

/** @internal Deterministic, collision-safe carrier class allocation. */
final class GeometryCarrierClassAllocator
{
/** @var callable(string): string */
private $digest;

/** @var array<string, string> */
private array $classBySignature = array();

/** @var array<string, string> */
private array $signatureByClass = array();

/** @param callable(string): string|null $digest */
public function __construct(?callable $digest = null)
{
$this->digest = $digest ?? static fn (string $value): string => hash('sha256', $value);
}

public function allocate(string $signature): string
{
if (isset($this->classBySignature[$signature])) {
return $this->classBySignature[$signature];
}

$base = 'be-inline-geometry-' . ($this->digest)($signature);
$className = $base;
$attempt = 0;
while (isset($this->signatureByClass[$className]) && $this->signatureByClass[$className] !== $signature) {
++$attempt;
$className = $base . '-' . hash('sha256', $signature . ':' . $attempt);
}

$this->classBySignature[$signature] = $className;
$this->signatureByClass[$className] = $signature;
return $className;
}
}
Loading
Loading