CssCompressor.php 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. <?php
  2. /**
  3. * Class Minify_YUI_CssCompressor
  4. * @package Minify
  5. *
  6. * YUI Compressor
  7. * Author: Julien Lecomte - http://www.julienlecomte.net/
  8. * Author: Isaac Schlueter - http://foohack.com/
  9. * Author: Stoyan Stefanov - http://phpied.com/
  10. * Author: Steve Clay - http://www.mrclay.org/ (PHP port)
  11. * Copyright (c) 2009 Yahoo! Inc. All rights reserved.
  12. * The copyrights embodied in the content of this file are licensed
  13. * by Yahoo! Inc. under the BSD (revised) open source license.
  14. */
  15. /**
  16. * Compress CSS (incomplete DO NOT USE)
  17. *
  18. * @see https://github.com/yui/yuicompressor/blob/master/src/com/yahoo/platform/yui/compressor/CssCompressor.java
  19. *
  20. * @package Minify
  21. */
  22. class Minify_YUI_CssCompressor {
  23. /**
  24. * Minify a CSS string
  25. *
  26. * @param string $css
  27. *
  28. * @return string
  29. */
  30. public function compress($css, $linebreakpos = 0)
  31. {
  32. $css = str_replace("\r\n", "\n", $css);
  33. /**
  34. * @todo comment removal
  35. * @todo re-port from newer Java version
  36. */
  37. // Normalize all whitespace strings to single spaces. Easier to work with that way.
  38. $css = preg_replace('@\s+@', ' ', $css);
  39. // Make a pseudo class for the Box Model Hack
  40. $css = preg_replace("@\"\\\\\"}\\\\\"\"@", "___PSEUDOCLASSBMH___", $css);
  41. // Remove the spaces before the things that should not have spaces before them.
  42. // But, be careful not to turn "p :link {...}" into "p:link{...}"
  43. // Swap out any pseudo-class colons with the token, and then swap back.
  44. $css = preg_replace_callback("@(^|\\})(([^\\{:])+:)+([^\\{]*\\{)@", array($this, '_removeSpacesCB'), $css);
  45. $css = preg_replace("@\\s+([!{};:>+\\(\\)\\],])@", "$1", $css);
  46. $css = str_replace("___PSEUDOCLASSCOLON___", ":", $css);
  47. // Remove the spaces after the things that should not have spaces after them.
  48. $css = preg_replace("@([!{}:;>+\\(\\[,])\\s+@", "$1", $css);
  49. // Add the semicolon where it's missing.
  50. $css = preg_replace("@([^;\\}])}@", "$1;}", $css);
  51. // Replace 0(px,em,%) with 0.
  52. $css = preg_replace("@([\\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)@", "$1$2", $css);
  53. // Replace 0 0 0 0; with 0.
  54. $css = str_replace(":0 0 0 0;", ":0;", $css);
  55. $css = str_replace(":0 0 0;", ":0;", $css);
  56. $css = str_replace(":0 0;", ":0;", $css);
  57. // Replace background-position:0; with background-position:0 0;
  58. $css = str_replace("background-position:0;", "background-position:0 0;", $css);
  59. // Replace 0.6 to .6, but only when preceded by : or a white-space
  60. $css = preg_replace("@(:|\\s)0+\\.(\\d+)@", "$1.$2", $css);
  61. // Shorten colors from rgb(51,102,153) to #336699
  62. // This makes it more likely that it'll get further compressed in the next step.
  63. $css = preg_replace_callback("@rgb\\s*\\(\\s*([0-9,\\s]+)\\s*\\)@", array($this, '_shortenRgbCB'), $css);
  64. // Shorten colors from #AABBCC to #ABC. Note that we want to make sure
  65. // the color is not preceded by either ", " or =. Indeed, the property
  66. // filter: chroma(color="#FFFFFF");
  67. // would become
  68. // filter: chroma(color="#FFF");
  69. // which makes the filter break in IE.
  70. $css = preg_replace_callback("@([^\"'=\\s])(\\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])@", array($this, '_shortenHexCB'), $css);
  71. // Remove empty rules.
  72. $css = preg_replace("@[^\\}]+\\{;\\}@", "", $css);
  73. $linebreakpos = isset($this->_options['linebreakpos'])
  74. ? $this->_options['linebreakpos']
  75. : 0;
  76. if ($linebreakpos > 0) {
  77. // Some source control tools don't like it when files containing lines longer
  78. // than, say 8000 characters, are checked in. The linebreak option is used in
  79. // that case to split long lines after a specific column.
  80. $i = 0;
  81. $linestartpos = 0;
  82. $sb = $css;
  83. // make sure strlen returns byte count
  84. $mbIntEnc = null;
  85. if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
  86. $mbIntEnc = mb_internal_encoding();
  87. mb_internal_encoding('8bit');
  88. }
  89. $sbLength = strlen($css);
  90. while ($i < $sbLength) {
  91. $c = $sb[$i++];
  92. if ($c === '}' && $i - $linestartpos > $linebreakpos) {
  93. $sb = substr_replace($sb, "\n", $i, 0);
  94. $sbLength++;
  95. $linestartpos = $i;
  96. }
  97. }
  98. $css = $sb;
  99. // undo potential mb_encoding change
  100. if ($mbIntEnc !== null) {
  101. mb_internal_encoding($mbIntEnc);
  102. }
  103. }
  104. // Replace the pseudo class for the Box Model Hack
  105. $css = str_replace("___PSEUDOCLASSBMH___", "\"\\\\\"}\\\\\"\"", $css);
  106. // Replace multiple semi-colons in a row by a single one
  107. // See SF bug #1980989
  108. $css = preg_replace("@;;+@", ";", $css);
  109. // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
  110. $css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
  111. // Trim the final string (for any leading or trailing white spaces)
  112. $css = trim($css);
  113. return $css;
  114. }
  115. protected function _removeSpacesCB($m)
  116. {
  117. return str_replace(':', '___PSEUDOCLASSCOLON___', $m[0]);
  118. }
  119. protected function _shortenRgbCB($m)
  120. {
  121. $rgbcolors = explode(',', $m[1]);
  122. $hexcolor = '#';
  123. for ($i = 0; $i < count($rgbcolors); $i++) {
  124. $val = round($rgbcolors[$i]);
  125. if ($val < 16) {
  126. $hexcolor .= '0';
  127. }
  128. $hexcolor .= dechex($val);
  129. }
  130. return $hexcolor;
  131. }
  132. protected function _shortenHexCB($m)
  133. {
  134. // Test for AABBCC pattern
  135. if ((strtolower($m[3])===strtolower($m[4])) &&
  136. (strtolower($m[5])===strtolower($m[6])) &&
  137. (strtolower($m[7])===strtolower($m[8]))) {
  138. return $m[1] . $m[2] . "#" . $m[3] . $m[5] . $m[7];
  139. } else {
  140. return $m[0];
  141. }
  142. }
  143. }