MinimifierHtml.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. <?php
  2. namespace KarmaFW\App\Middlewares;
  3. use \KarmaFW\App\Request;
  4. use \KarmaFW\App\Response;
  5. class MinimifierHtml
  6. {
  7. protected $minimify_html;
  8. protected $minimify_external_js;
  9. protected $minimify_external_css;
  10. public function __construct($minimify_html=true, $minimify_external_js=true, $minimify_external_css=true)
  11. {
  12. $this->minimify_html = $minimify_html;
  13. $this->minimify_external_js = $minimify_external_js;
  14. $this->minimify_external_css = $minimify_external_css;
  15. }
  16. public function __invoke(Request $request, Response $response, callable $next)
  17. {
  18. $response = $next($request, $response);
  19. $content_type = $response->getContentType();
  20. $content_type_short = explode(';', $content_type)[0];
  21. if ($content_type_short !== 'text/html') {
  22. return $response;
  23. }
  24. if ($this->minimify_external_js || $this->minimify_external_css) {
  25. // modification à la volée des liens CSS & JS
  26. $content = $response->getContent();
  27. if ($this->minimify_external_css) {
  28. // modify CSS link files in HTML content
  29. preg_match_all('#<link[^>]+"(/assets/css/[^">]+.css)"[^>]*>#', $content, $regs);
  30. $css_files = $regs[1];
  31. $suffix = '.phpmin.css';
  32. foreach ($css_files as $css_file) {
  33. if (substr($css_file, -8) != '.min.css' && substr($css_file, -11) != '.phpmin.css') {
  34. $replacement = '\1' . $css_file . $suffix . '\2';
  35. $content = preg_replace('#(<link [^>]+")' . preg_quote($css_file) . '("[^>]*>)#', $replacement, $content);
  36. }
  37. }
  38. }
  39. if ($this->minimify_external_js) {
  40. // modify JS link files in HTML content
  41. preg_match_all('#<script[^>]+"(/assets/js/[^">]+.js)"[^>]*>#', $content, $regs);
  42. $js_files = $regs[1];
  43. $suffix = '.phpmin.js';
  44. foreach ($js_files as $js_file) {
  45. if (substr($js_file, -7) != '.min.js' && substr($js_file, -10) != '.phpmin.js') {
  46. $replacement = '\1' . $js_file . $suffix . '\2';
  47. $content = preg_replace('#(<script[^>]+")' . preg_quote($js_file) . '("[^>]*>)#', $replacement, $content);
  48. }
  49. }
  50. }
  51. $response->setContent($content);
  52. }
  53. if ($this->minimify_html) {
  54. // minimify HTML
  55. $content = $response->getContent();
  56. $content_length = $response->getContentLength();
  57. $content_minimified = self::minify_html($content);
  58. $response->setContent($content_minimified);
  59. $content_minimified_length = $response->getContentLength();
  60. // add information headers
  61. $response->addHeader('X-HTML-Unminimified-Content-Length', $content_length);
  62. $response->addHeader('X-HTML-Minimified-Content-Length', $content_minimified_length);
  63. }
  64. return $response;
  65. }
  66. // HTML Minifier (source: https://gist.github.com/Rodrigo54/93169db48194d470188f )
  67. public static function minify_html($input) {
  68. if(trim($input) === "") return $input;
  69. // Remove extra white-space(s) between HTML attribute(s)
  70. $input = preg_replace_callback('#<([^\/\s<>!]+)(?:\s+([^<>]*?)\s*|\s*)(\/?)>#s', function($matches) {
  71. return '<' . $matches[1] . preg_replace('#([^\s=]+)(\=([\'"]?)(.*?)\3)?(\s+|$)#s', ' $1$2', $matches[2]) . $matches[3] . '>';
  72. }, str_replace("\r", "", $input));
  73. // Minify inline CSS declaration(s)
  74. if(strpos($input, ' style=') !== false) {
  75. $input = preg_replace_callback('#<([^<]+?)\s+style=([\'"])(.*?)\2(?=[\/\s>])#s', function($matches) {
  76. return '<' . $matches[1] . ' style=' . $matches[2] . MinimifierCss::minify_css($matches[3]) . $matches[2];
  77. }, $input);
  78. }
  79. if(strpos($input, '</style>') !== false) {
  80. $input = preg_replace_callback('#<style(.*?)>(.*?)</style>#is', function($matches) {
  81. return '<style' . $matches[1] .'>'. MinimifierCss::minify_css($matches[2]) . '</style>';
  82. }, $input);
  83. }
  84. if(strpos($input, '</script>') !== false) {
  85. $input = preg_replace_callback('#<script(.*?)>(.*?)</script>#is', function($matches) {
  86. return '<script' . $matches[1] .'>'. MinimifierJs::minify_js($matches[2]) . '</script>';
  87. }, $input);
  88. }
  89. return preg_replace(
  90. array(
  91. // t = text
  92. // o = tag open
  93. // c = tag close
  94. // Keep important white-space(s) after self-closing HTML tag(s)
  95. '#<(img|input)(>| .*?>)#s',
  96. // Remove a line break and two or more white-space(s) between tag(s)
  97. '#(<!--.*?-->)|(>)(?:\n*|\s{2,})(<)|^\s*|\s*$#s',
  98. '#(<!--.*?-->)|(?<!\>)\s+(<\/.*?>)|(<[^\/]*?>)\s+(?!\<)#s', // t+c || o+t
  99. '#(<!--.*?-->)|(<[^\/]*?>)\s+(<[^\/]*?>)|(<\/.*?>)\s+(<\/.*?>)#s', // o+o || c+c
  100. '#(<!--.*?-->)|(<\/.*?>)\s+(\s)(?!\<)|(?<!\>)\s+(\s)(<[^\/]*?\/?>)|(<[^\/]*?\/?>)\s+(\s)(?!\<)#s', // c+t || t+o || o+t -- separated by long white-space(s)
  101. '#(<!--.*?-->)|(<[^\/]*?>)\s+(<\/.*?>)#s', // empty tag
  102. '#<(img|input)(>| .*?>)<\/\1>#s', // reset previous fix
  103. '#(&nbsp;)&nbsp;(?![<\s])#', // clean up ...
  104. '#(?<=\>)(&nbsp;)(?=\<)#', // --ibid
  105. // Remove HTML comment(s) except IE comment(s)
  106. '#\s*<!--(?!\[if\s).*?-->\s*|(?<!\>)\n+(?=\<[^!])#s'
  107. ),
  108. array(
  109. '<$1$2</$1>',
  110. '$1$2$3',
  111. '$1$2$3',
  112. '$1$2$3$4$5',
  113. '$1$2$3$4$5$6$7',
  114. '$1$2$3',
  115. '<$1$2',
  116. '$1 ',
  117. '$1',
  118. ""
  119. ),
  120. $input);
  121. }
  122. }