PageRenderTime 60ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/csspp.php

https://github.com/jeremyboles/csspp
PHP | 301 lines | 221 code | 39 blank | 41 comment | 14 complexity | 596d4a1495414ac0b6e38a9b9acc3426 MD5 | raw file
  1. <?php
  2. class CSSPP
  3. {
  4. // Regex for CSS expanders. Order in this array matters.
  5. static public $expanders = array(
  6. '#\s*appearance\s*:\s*(.+);#i' => '$0 -moz-appearance: $1; -o-appearace: $1; -webkit-appearance: $1;',
  7. '#\s*background\-image\s*:\s*gradient\((.+)\)\s*;#' => 'background-image: gradient($1); background-image: -moz-gradient($1); background-image: -o-gradient($1); background-image: -webkit-gradient($1);',
  8. '#\s*background\s*:.+gradient\((.+)\).+;#' => '$0 background-image: -moz-gradient($1); background-image: -o-gradient($1); background-image: -webkit-gradient($1);',
  9. '#\s*background\-clip\s*:\s*(.+);#i' => '$0 -moz-background-clip: $1; -o-background-clip: $1; -webkit-background-clip: $1;',
  10. '#\s*background\-origin\s*:\s*(.+);#i' => '$0 -moz-background-origin: $1; -o-background-origin: $1; -webkit-background-origin: $1;',
  11. '#\s*background\-size\s*:\s*(.+);#i' => '$0 -moz-background-size: $1; -o-background-size: $1; -webkit-background-size: $1;',
  12. '#\s*border\-radius\s*:\s*(.+);#i' => '$0 -moz-border-radius: $1; -o-border-radius: $1; -webkit-border-radius: $1;',
  13. '#\s*border-bottom-left-radius\s*:\s*(.+);#i' => '$0 -moz-border-radius-bottomleft: $1; -o-border-bottom-left-radius: $1; -webkit-border-bottom-left-radius: $1;',
  14. '#\s*border-bottom-right-radius\s*:\s*(.+);#i' => '$0 -moz-border-radius-bottomright: $1; -o-border-bottom-right-radius: $1; -webkit-border-bottom-right-radius: $1;',
  15. '#\s*border-top-left-radius\s*:\s*(.+);#i' => '$0 -moz-border-radius-topleft: $1; -o-border-top-left-radius: $1; -webkit-border-top-left-radius: $1;',
  16. '#\s*border-top-right-radius\s*:\s*(.+);#i' => '$0 -moz-border-radius-topright: $1; -o-border-top-right-radius: $1; -webkit-border-top-right-radius: $1;',
  17. '#\s*box\-shadow\s*:\s*(.+);#i' => '$0 -moz-box-shadow: $1; -o-box-shadow: $1; -webkit-box-shadow: $1;',
  18. '#\s*box\-sizing\s*:\s*(.+);#i' => '$0 -moz-box-sizing: $1; -o-box-sizing: $1; -webkit-box-sizing: $1;',
  19. '#\s*transition\s*:\s*(.+);#i' => '$0 -moz-transition: $1; -o-transition: $1 -webkit-transition: $1;',
  20. '#\s*transition-duration\s*:\s*(.+);#i' => '$0 -moz-transition-duration: $1; -o-transition-duration: $1; -webkit-transition-duration: $1;',
  21. '#\s*transition-property\s*:\s*(.+);#i' => '$0 -moz-transition-property: $1; -o-transition-property: $1; -webkit-transition-property: $1;',
  22. '#\s*transition-timing-function\s*:\s*(.+);#i' => '$0 -moz-transition-timing-function: $1; -o-transition-timing-function: $1; -webkit-transition-timing-function: $1;',
  23. '#\s*user-drag\s*:\s*(.+);#i' => '$0 -moz-user-drag: $1; -o-user-drag: $1; -webkit-user-drag: $1;',
  24. '#\s*user-select\s*:\s*(.+);#i' => '$0 -moz-user-select: $1; -o-user-select: $1; -webkit-user-select: $1;'
  25. );
  26. private $base_dir;
  27. private $css;
  28. private $file;
  29. private $included = array();
  30. private $options = array(
  31. 'bases' => TRUE,
  32. 'comments' => FALSE,
  33. 'expanders' => TRUE,
  34. 'includes' => TRUE,
  35. 'imports' => TRUE,
  36. 'minify' => TRUE,
  37. 'variables' => TRUE
  38. );
  39. // Constructor method
  40. // Requires a file and a base directory
  41. function __construct($file, $base_dir, $options=array())
  42. {
  43. // Our intial file
  44. $file_path = $base_dir . $file;
  45. // Intialize instance variables
  46. $this->file = $file;
  47. $this->base_dir = $base_dir;
  48. $this->css_dir = dirname($file_path);
  49. // Set any options passed in
  50. $this->setOptions($options);
  51. // Read the intial CSS file into an instance variable
  52. $this->css = file_get_contents($file_path);
  53. }
  54. // Magic method for outputing the CSS using an echo statement
  55. public function __toString()
  56. {
  57. return $this->process();
  58. }
  59. // Getter for css
  60. public function css()
  61. {
  62. return $this->css;
  63. }
  64. // Prepend some text (Hopefully CSS) to the beginning of the CSS
  65. public function prepend($css)
  66. {
  67. $this->css = $css . $this->css;
  68. }
  69. // Appends some text (Hopefully CSS) to the end of the CSS
  70. public function append($css)
  71. {
  72. $this->css .= $css;
  73. }
  74. // Replace all instances of @include with the contents of the file
  75. private function includeExternals()
  76. {
  77. // Find all instances of @include
  78. preg_match_all('#@include\s*\'(.+)\';#i', $this->css, $found);
  79. // Search through all of the found instances of @include and replace it
  80. // with the contents of the file it references
  81. foreach($found[1] as $i => $include)
  82. {
  83. $file = preg_replace('#^("|\')|("|\')$#', '', $include);
  84. // Make sure we haven't included the file already
  85. if (!in_array($file, $this->included))
  86. {
  87. if (substr($file, 0, 1) == '/')
  88. {
  89. $file_name = $file;
  90. }
  91. else
  92. {
  93. $file_name = $this->css_dir . '/' . $file;
  94. }
  95. $css = file_get_contents($file_name);
  96. $this->css = str_replace($found[0][$i], $css, $this->css);
  97. array_push($this->included, $file);
  98. }
  99. else
  100. {
  101. $this->css = str_replace($found[0][$i], '', $this->css);
  102. }
  103. }
  104. }
  105. // Replace all instances of @import with the contents of the file recursivelly
  106. private function importExternals()
  107. {
  108. // Find all instances of @include
  109. preg_match_all('#@import\s*"(.+)";#i', $this->css, $found);
  110. // Search through all of the found instances of @include and replace it
  111. // with the contents of the file it references
  112. foreach($found[1] as $i => $include)
  113. {
  114. $file = preg_replace('#^("|\')|("|\')$#', '', $include);
  115. // Make sure we haven't included the file already
  116. if (!in_array($file, $this->included))
  117. {
  118. $base_dir = $this->css_dir . DS;
  119. if (substr($file, 0, 1) == '/')
  120. {
  121. $base_dir = $this->base_dir;
  122. }
  123. $filename = realpath($base_dir . $file);
  124. $csspp = new CSSPP(basename($filename), dirname($filename) . DS, $this->options);
  125. $css = (string)$csspp;
  126. $this->css = str_replace($found[0][$i], $css, $this->css);
  127. array_push($this->included, $file);
  128. }
  129. else
  130. {
  131. $this->css = str_replace($found[0][$i], '', $this->css);
  132. }
  133. }
  134. }
  135. // Remove any extra, unneed stuff from the CSS to make it as small as possible
  136. private function minify()
  137. {
  138. $this->removeWhitespace();
  139. $this->removeLastSemicolon();
  140. }
  141. // Processed the CSS
  142. public function process()
  143. {
  144. if ($this->options['includes'])
  145. {
  146. $this->includeExternals();
  147. }
  148. if ($this->options['imports'])
  149. {
  150. $this->importExternals();
  151. }
  152. if (!$this->options['comments'])
  153. {
  154. $this->removeComments();
  155. }
  156. if ($this->options['variables'])
  157. {
  158. $this->replaceVariables();
  159. }
  160. if ($this->options['expanders'])
  161. {
  162. $this->replaceExpanders();
  163. }
  164. if ($this->options['bases'])
  165. {
  166. $this->replaceBases();
  167. }
  168. if ($this->options['minify'])
  169. {
  170. $this->minify();
  171. }
  172. return $this->css;
  173. }
  174. // Remove all of the comments from the CSS file
  175. private function removeComments()
  176. {
  177. $this->css = preg_replace('#\/\*[\d\D]*?\*\/|\t+#', '', $this->css);
  178. }
  179. // Remove the last semicolon of each selector block
  180. private function removeLastSemicolon()
  181. {
  182. $this->css = str_replace(';}', '}', $this->css);
  183. }
  184. // Remove extra spaces in the CSS
  185. private function removeWhitespace()
  186. {
  187. $this->css = str_replace(array("\n", "\r", "\t"), '', $this->css);
  188. $this->css = preg_replace('/\s\s+/', '', $this->css);
  189. $this->css = preg_replace('/\s*({|}|\[|\]|=|~|\+|>|\||;|:|,)\s*/', '$1', $this->css);
  190. }
  191. // Find the bases and psuedo bases and replace the based-on statements
  192. private function replaceBases()
  193. {
  194. // Find all of the bases with psuedo elements
  195. preg_match_all('#@base\(([^\s]+)\)\:([^\s\{]+)\s*\{(\s*[^\}]+)\s*\}\s*#i', $this->css, $found);
  196. foreach ($found[0] as $i => $base)
  197. {
  198. // Find all of the based-on statements
  199. preg_match_all("#\s*([^\}]+)\s*\{[^\{]+based\-on\s*:\s*base\({$found[1][$i]}\)\s*;\s*[^\}]+\s*\}#", $this->css, $property);
  200. foreach ($property[0] as $j => $parent)
  201. {
  202. // Apply the psudeo element to multiple selects
  203. $selectors = explode(',', $property[1][$j]);
  204. for ($k = 0; $k < count($selectors); $k++)
  205. {
  206. $selectors[$k] = trim($selectors[$k]) . ':' . $found[2][$i];
  207. }
  208. $selectors = implode(', ', $selectors);
  209. // And apply the properties to the psuedo bases
  210. $this->css = str_replace($property[0][$j], "{$property[0][$j]} $selectors {{$found[3][$i]}}", $this->css);
  211. }
  212. // Remove the psuedo base from the CSS
  213. $this->css = str_replace($base, '', $this->css);
  214. }
  215. // Find all of the plain-old base statements
  216. preg_match_all('#@base\(([^\s\{]+)\)\s*\{(\s*[^\}]+)\s*\}\s*#i', $this->css, $found);
  217. foreach ($found[0] as $i => $base)
  218. {
  219. $this->css = str_replace($base, '', $this->css);
  220. $this->css = preg_replace("#based\-on\s*:\s*base\({$found[1][$i]}\)\s*;\s*#", trim($found[2][$i]), $this->css);
  221. }
  222. }
  223. // Iterates through the css expanders and replaces them
  224. private function replaceExpanders()
  225. {
  226. foreach (self::$expanders as $expander => $replacer)
  227. {
  228. $this->css = preg_replace($expander, $replacer, $this->css);
  229. }
  230. }
  231. // Find the variable declarations and replace the variables
  232. private function replaceVariables()
  233. {
  234. // Find all of the variable declaration groups
  235. preg_match_all('#@variables\s*\{\s*([^\}]+)\s*\}\s*#i', $this->css, $found);
  236. foreach ($found[0] as $i => $variable_group)
  237. {
  238. // Remove the variable group from the css
  239. $this->css = str_replace($variable_group, '', $this->css);
  240. // Find the individual variables
  241. preg_match_all('#([_a-z0-9\-]+)\s*:\s*([^;]+);#i', $found[1][$i], $variables);
  242. foreach ($variables[1] as $variable => $name)
  243. {
  244. // Replace the variables within the CSS
  245. $this->css = str_replace("var($name)", $variables[2][$variable], $this->css);
  246. }
  247. }
  248. }
  249. // Set a processor option
  250. public function setOption($key, $value)
  251. {
  252. $this->options[$key] = $value;
  253. }
  254. // Set multiple processor option at once
  255. public function setOptions($options)
  256. {
  257. foreach ($options as $key => $value)
  258. {
  259. $this->setOption($key, $value);
  260. }
  261. }
  262. }