View source
<?php
namespace Drupal\minifyhtml\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class MinifyHTMLExit implements EventSubscriberInterface {
protected $content;
protected $placeholders = [];
protected $token;
protected $config;
protected $time;
protected $logger;
protected $abort = FALSE;
public function __construct() {
$arg_error = TRUE;
if (func_num_args() == 3) {
$this->config = func_get_arg(0);
$this->time = func_get_arg(1);
$this->logger = func_get_arg(2);
if ($this->config) {
$this->token = 'MINIFYHTML_' . md5($this->time
->getRequestTime());
$arg_error = FALSE;
}
}
if ($arg_error) {
\Drupal::messenger()
->addWarning(t('Minify HTML has been disabled until the cache has been cleared.'));
$this->abort = TRUE;
}
}
public function response(FilterResponseEvent $event) {
if (!$this->abort && $this->config
->get('minifyhtml.config')
->get('minify')) {
$response = $event
->getResponse();
$allowed_response_classes = [
'Drupal\\big_pipe\\Render\\BigPipeResponse',
'Drupal\\Core\\Render\\HtmlResponse',
];
if (in_array(get_class($response), $allowed_response_classes)) {
$this->content = $response
->getContent();
$this
->minify();
if (strpos($this->content, '%' . $this->token)) {
$this->logger
->get('minifyhtml')
->warning('Minifyhtml failed.');
}
else {
$response
->setContent($this->content);
}
}
}
}
public static function getSubscribedEvents() {
$events = [];
$events[KernelEvents::RESPONSE][] = [
'response',
-10000,
];
return $events;
}
protected function minify() {
$callbacks = [
'minifyhtmlPlaceholderCallbackTextarea' => '/\\s*<textarea(\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i',
'minifyhtmlPlaceholderCallbackPre' => '/\\s*<pre(\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i',
'minifyhtmlPlaceholderCallbackIframe' => '/\\s*<iframe(\\b[^>]*?>[\\s\\S]*?<\\/iframe>)\\s*/i',
'minifyhtmlPlaceholderCallbackScript' => '/\\s*<script(\\b[^>]*?>[\\s\\S]*?<\\/script>)\\s*/i',
'minifyhtmlPlaceholderCallbackStyle' => '/\\s*<style(\\b[^>]*?>[\\s\\S]*?<\\/style>)\\s*/i',
];
if ($this->config
->get('minifyhtml.config')
->get('strip_comments')) {
$callbacks['minifyhtmlRemoveHtmlComment'] = '/<!--([\\s\\S]*?)-->/';
}
foreach ($callbacks as $callback => $pattern) {
$content = $this
->minifyhtmlCallback($pattern, $callback);
if (!is_null($content)) {
$this->content = $content;
}
}
$this
->minifyHtml();
if (!empty($this->placeholders)) {
$this->content = str_replace(array_keys($this->placeholders), array_values($this->placeholders), $this->content);
}
}
protected function minifyhtmlCallback($pattern, $callback) {
$content = preg_replace_callback($pattern, [
$this,
$callback,
], $this->content);
if ($error = preg_last_error()) {
$this->logger
->get('minifyhtml')
->error('Preg error. The error code is @error. You can view what this error code is by viewing http://php.net/manual/en/function.preg-last-error.php', [
'@error' => $error,
]);
}
return $content;
}
protected function minifyhtmlPlaceholderCallbackTextarea(array $matches) {
return $this
->minifyPlaceholderReplace(trim($matches[0]));
}
protected function minifyhtmlPlaceholderCallbackPre(array $matches) {
return $this
->minifyPlaceholderReplace(trim($matches[0]));
}
protected function minifyhtmlPlaceholderCallbackIframe(array $matches) {
$iframe = preg_replace('/^\\s+|\\s+$/m', '', $matches[0]);
return $this
->minifyPlaceholderReplace(trim($iframe));
}
protected function minifyhtmlPlaceholderCallbackScript(array $matches) {
$search = [];
$replace = [];
if ($this->config
->get('minifyhtml.config')
->get('strip_comments')) {
$search[] = '!/\\*.*?\\*/!s';
$replace[] = '';
}
$search[] = '/^\\s+|\\s+$/m';
$replace[] = "\n";
$search[] = '/\\n(\\s*\\n)+/';
$replace[] = "\n";
$script = preg_replace($search, $replace, $matches[0]);
return $this
->minifyPlaceholderReplace(trim($script));
}
protected function minifyhtmlPlaceholderCallbackStyle(array $matches) {
$search = [];
$replace = [];
if ($this->config
->get('minifyhtml.config')
->get('strip_comments')) {
$search[] = '!/\\*.*?\\*/!s';
$replace[] = '';
}
$search[] = '/^\\s+|\\s+$/m';
$replace[] = '';
$style = preg_replace($search, $replace, $matches[0]);
return $this
->minifyPlaceholderReplace(trim($style));
}
protected function minifyPlaceholderReplace($content) {
$placeholder = '%' . $this->token . count($this->placeholders) . '%';
$this->placeholders[$placeholder] = $content;
return $placeholder;
}
protected function minifyhtmlRemoveHtmlComment(array $matches) {
return 0 === strpos($matches[1], '[') || FALSE !== strpos($matches[1], '<![') ? $matches[0] : '';
}
protected function minifyHtml() {
$search = [];
$replace = [];
$search[] = '/\\>[^\\S ]+/s';
$replace[] = '>';
$search[] = '/[^\\S ]+\\</s';
$replace[] = '<';
$search[] = '/(\\s)+/s';
$replace[] = '\\1';
$search[] = '/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body' . '|caption|center|col(?:group)?|dd|dir|div|dl|dt|fieldset|form' . '|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta' . '|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)' . '|ul)\\b[^>]*>)/i';
$replace[] = '$1';
$search[] = '/^\\s+|\\s+$/m';
$replace[] = '';
$minified = preg_replace($search, $replace, $this->content);
if (PREG_NO_ERROR === preg_last_error()) {
$this->content = $minified;
}
}
}