ssml-1.x-dev/src/Processor.php
src/Processor.php
<?php
declare(strict_types=1);
namespace Drupal\ssml;
use Drupal\Core\Extension\ModuleExtensionList;
/**
* Converts HTML to SSML.
*/
class Processor {
/**
* Creates the processor.
*
* @param \Drupal\Core\Extension\ModuleExtensionList $moduleExtensionList
* The module extension list service.
*/
public function __construct(
protected readonly ModuleExtensionList $moduleExtensionList,
) {}
/**
* Converts HTML to SSML.
*
* @param string $html
* The HTML.
* @param string|null $charset
* An optional character set to prepend to the HTML before parsing.
* @param array<mixed> $context
* An array of context. Doesn't do anything yet, but can be used to tailor
* output based on provider.
*
* @return string
* An SSML string.
*
* @throws \RuntimeException
* On error.
*/
public function htmlToSsml(string $html, ?string $charset = NULL, array $context = []): string {
// @todo: Use \Dom\HTMLDocument when we can require PHP 8.4.
// @todo: See if we can use masterminds/html5 in the meantime?
$old_use_errors = libxml_use_internal_errors(true);
try {
$xslt_processor = new \XSLTProcessor();
$xslt = new \DOMDocument();
$xslt->loadXML($this->getXslt());
if ($charset) {
$html = "<meta charset=\"$charset\">$html";
}
$html_doc = new \DOMDocument();
$html_doc->loadHTML($html);
$result = $xslt_processor->importStyleSheet($xslt);
if (!$result) {
$message = $this->getXmlErrorsMessage();
throw new \RuntimeException("Unable to transform to SSML: $message");
}
$ssml = $xslt_processor->transformToXML($html_doc);
if ($ssml === FALSE) {
$message = $this->getXmlErrorsMessage();
throw new \RuntimeException("Unable to transform to SSML: $message");
}
return '<speak>' . $ssml . '</speak>';
}
finally {
libxml_clear_errors();
libxml_use_internal_errors($old_use_errors);
}
}
/**
* Get the SSML transformation as an XSLT string.
*
* @return string
*/
protected function getXslt(): string {
$path = $this->moduleExtensionList->getPath('ssml') . '/xslt/default.xslt';
return file_get_contents($path) ?: throw new \RuntimeException("Couldn't read $path.");
}
/**
* Returns any XML errors as a single string.
*
* @return string
*/
protected function getXmlErrorsMessage(): string {
$messages = array_map(fn(\LibXMLError $error): string => "$error->message ($error->file:$error->line)", libxml_get_errors());
return implode(PHP_EOL, $messages);
}
}
