protectMagentoDirectives($html);
$document = $this->createDocument($protectedHtml);
$xpath = new DOMXPath($document);
$query = sprintf(
'//*[@id="%s"]//text()[normalize-space(.) != "" and not(ancestor::script) and not(ancestor::style) and not(ancestor::code) and not(ancestor::pre)]',
self::ROOT_ID
);
$nodes = $xpath->query($query);
$textNodes = [];
$texts = [];
$index = 0;
if ($nodes !== false) {
foreach ($nodes as $node) {
if (!$node instanceof DOMText) {
continue;
}
$text = $this->restoreMagentoDirectives($node->nodeValue ?? '', $directivePlaceholders);
if (trim($text) === '') {
continue;
}
$id = 'html_' . $index++;
$textNodes[$id] = $node;
$texts[$id] = $text;
}
}
$root = $document->getElementById(self::ROOT_ID);
if (!$root) {
throw new \RuntimeException('Unable to parse HTML fragment.');
}
return new HtmlFragment($document, $root, $textNodes, $texts, $directivePlaceholders);
}
private function createDocument(string $html): DOMDocument
{
$document = new DOMDocument('1.0', 'UTF-8');
$wrappedHtml = sprintf(
'
%s
',
self::ROOT_ID,
$html
);
$previous = libxml_use_internal_errors(true);
$loaded = $document->loadHTML($wrappedHtml, LIBXML_HTML_NODEFDTD | LIBXML_HTML_NOIMPLIED);
libxml_clear_errors();
libxml_use_internal_errors($previous);
if (!$loaded) {
throw new \RuntimeException('Unable to parse HTML fragment.');
}
return $document;
}
/**
* @return array{0: string, 1: array}
*/
private function protectMagentoDirectives(string $html): array
{
$placeholders = [];
$index = 0;
$protected = preg_replace_callback(
'/\{\{.*?\}\}/s',
function (array $matches) use (&$placeholders, &$index): string {
$token = self::DIRECTIVE_TOKEN_PREFIX . $index++ . '__';
$placeholders[$token] = $this->normalizeMagentoDirectiveQuotes($matches[0]);
return $token;
},
$html
);
return [$protected ?? $html, $placeholders];
}
/**
* @param array $placeholders
*/
private function restoreMagentoDirectives(string $html, array $placeholders): string
{
$restored = $placeholders === [] ? $html : strtr($html, $placeholders);
return $this->normalizeMagentoDirectiveQuotes($restored);
}
private function normalizeMagentoDirectiveQuotes(string $html): string
{
return preg_replace_callback(
'/\{\{.*?\}\}/s',
static function (array $matches): string {
return str_ireplace(
['"', '"', '"', ''', ''', '''],
['"', '"', '"', "'", "'", "'"],
$matches[0]
);
},
$html
) ?? $html;
}
}