PageRenderTime 51ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/scaffold/libraries/Scaffold/CSS.php

https://github.com/rmdort/csscaffold
PHP | 371 lines | 195 code | 45 blank | 131 comment | 14 complexity | 36d299a2391d58e4c2e8ebda8b1bd029 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * CSS Utilities
  4. *
  5. * Has methods for interacting with the CSS string
  6. * and makes it very easy to find properties and values within the css
  7. *
  8. * @package CSScaffold
  9. * @author Anthony Short
  10. */
  11. class Scaffold_CSS
  12. {
  13. /**
  14. * Server path to this CSS file
  15. *
  16. * @var string
  17. */
  18. public $path;
  19. /**
  20. * The name of this CSS file
  21. *
  22. * @var string
  23. */
  24. public $file;
  25. /**
  26. * The string of CSS code
  27. *
  28. * @var string
  29. */
  30. public $string;
  31. /**
  32. * Constructor
  33. *
  34. * @param $file
  35. * @return void
  36. */
  37. public function __construct($file)
  38. {
  39. $this->path = dirname($file);
  40. $this->file = $file;
  41. $this->string = $this->remove_inline_comments(file_get_contents($file));
  42. }
  43. /**
  44. * Returns the CSS string when treated as a string
  45. *
  46. * @return string
  47. */
  48. public function __toString()
  49. {
  50. return $this->string;
  51. }
  52. /**
  53. * Compresses down the CSS file. Not a complete compression,
  54. * but enough to minimize parsing time.
  55. *
  56. * @return string $css
  57. */
  58. public function compress($css)
  59. {
  60. # Remove comments
  61. $this->string = $this->remove_comments($this->string);
  62. # Remove extra white space
  63. $this->string = preg_replace('/\s+/', ' ', $css);
  64. # Remove line breaks
  65. $this->string = preg_replace('/\n|\r/', '', $css);
  66. }
  67. /**
  68. * Removes inline comments
  69. *
  70. * @return return type
  71. */
  72. public function remove_inline_comments($css)
  73. {
  74. return preg_replace('#(\s|^)//.*$#Umsi', '', $css);
  75. }
  76. /**
  77. * Removes css comments
  78. *
  79. * @return string $css
  80. */
  81. public function remove_comments($css)
  82. {
  83. $css = $this->convert_entities('encode', $css);
  84. $css = trim(preg_replace('#/\*[^*]*\*+([^/*][^*]*\*+)*/#', '', $css));
  85. $css = $this->convert_entities('decode', $css);
  86. $css = $this->remove_inline_comments($css);
  87. return $css;
  88. }
  89. /**
  90. * Finds CSS 'functions'. These are things like url(), embed() etc.
  91. *
  92. * @author Anthony Short
  93. * @param $name
  94. * @param $capture_group
  95. * @return array
  96. */
  97. public function find_functions($name, $capture_group = "")
  98. {
  99. $regex =
  100. "/
  101. {$name}
  102. (
  103. \s*\(\s*
  104. ( (?: (?1) | [^()]+ )* )
  105. \s*\)\s*
  106. )
  107. /sx";
  108. if(preg_match_all($regex, $this->string, $match))
  109. {
  110. return ($capture_group == "") ? $match : $match[$capture_group];
  111. }
  112. else
  113. {
  114. return array();
  115. }
  116. }
  117. /**
  118. * Finds @groups within the css and returns
  119. * an array with the values, and groups.
  120. *
  121. * @author Anthony Short
  122. * @param $group string
  123. * @param $css string
  124. */
  125. public function find_at_group($group, $remove = true)
  126. {
  127. $found = array();
  128. $regex =
  129. "/
  130. # Group name
  131. @{$group}
  132. # Flag
  133. (?:
  134. \(( [^)]*? )\)
  135. )?
  136. [^{]*?
  137. (
  138. ([0-9a-zA-Z\_\-\@*&]*?)\s*
  139. \{
  140. ( (?: [^{}]+ | (?2) )*)
  141. \}
  142. )
  143. /ixs";
  144. if(preg_match_all($regex, $this->string, $matches))
  145. {
  146. $found['groups'] = $matches[0];
  147. $found['flag'] = $matches[1];
  148. $found['content'] = $matches[4];
  149. foreach($matches[4] as $key => $value)
  150. {
  151. // Remove comments to prevent breaking it
  152. $value = $this->remove_comments($value);
  153. foreach(explode(";", substr($value, 0, -1)) as $value)
  154. {
  155. // Encode any colons inside quotations
  156. if( preg_match_all('/[\'"](.*?\:.*?)[\'"]/',$value,$m) )
  157. {
  158. $value = str_replace($m[0][0],str_replace(':','#COLON#',$m[0][0]),$value);
  159. }
  160. $value = explode(":", $value);
  161. // Make sure it's set
  162. if(isset($value[1]))
  163. {
  164. $found['values'][trim($value[0])] = str_replace('#COLON#', ':', Scaffold::unquote($value[1]));
  165. }
  166. }
  167. }
  168. // Remove the found @ groups
  169. if($remove === true)
  170. {
  171. $this->string = str_replace($found['groups'], array(), $this->string);
  172. }
  173. return $found;
  174. }
  175. return false;
  176. }
  177. /**
  178. * Finds selectors which contain a particular property
  179. *
  180. * @author Anthony Short
  181. * @param $css
  182. * @param $property string
  183. * @param $value string
  184. */
  185. public function find_selectors_with_property($property, $value = ".*?")
  186. {
  187. if(preg_match_all("/([^{}]*)\s*\{\s*[^}]*(".$property."\s*\:\s*(".$value.")\s*\;).*?\s*\}/sx", $this->string, $match))
  188. {
  189. return $match;
  190. }
  191. else
  192. {
  193. return array();
  194. }
  195. }
  196. /**
  197. * Finds all properties with a particular value
  198. *
  199. * @author Anthony Short
  200. * @param $property
  201. * @param $value
  202. * @param $css
  203. * @return array
  204. */
  205. public function find_properties_with_value($property, $value = ".*?")
  206. {
  207. # Make the property name regex-friendly
  208. $property = Scaffold_Utils::preg_quote($property);
  209. $regex = "/ ({$property}) \s*\:\s* ({$value}) /sx";
  210. if(preg_match_all($regex, $this->string, $match))
  211. {
  212. return $match;
  213. }
  214. else
  215. {
  216. return array();
  217. }
  218. }
  219. /**
  220. * Finds a selector and returns it as string
  221. *
  222. * @author Anthony Short
  223. * @param $selector string
  224. * @param $css string
  225. */
  226. public function find_selectors($selector, $recursive = "")
  227. {
  228. if($recursive != "")
  229. {
  230. $recursive = "|(?{$recursive})";
  231. }
  232. $regex =
  233. "/
  234. # This is the selector we're looking for
  235. ({$selector})
  236. # Return all inner selectors and properties
  237. (
  238. ([0-9a-zA-Z\_\-\*&]*?)\s*
  239. \{
  240. (?P<properties>(?:[^{}]+{$recursive})*)
  241. \}
  242. )
  243. /xs";
  244. if(preg_match_all($regex, $this->string, $match))
  245. {
  246. return $match;
  247. }
  248. else
  249. {
  250. return array();
  251. }
  252. }
  253. /**
  254. * Finds all properties within a css string
  255. *
  256. * @author Anthony Short
  257. * @param $property string
  258. * @param $css string
  259. */
  260. public function find_property($property)
  261. {
  262. if(preg_match_all('/('.Scaffold_Utils::preg_quote($property).')\s*\:\s*(.*?)\s*\;/sx', $this->string, $matches))
  263. {
  264. return (array)$matches;
  265. }
  266. else
  267. {
  268. return array();
  269. }
  270. }
  271. /**
  272. * Check if a selector exists
  273. *
  274. * @param $name
  275. * @return boolean
  276. */
  277. public function selector_exists($name)
  278. {
  279. return preg_match('/'.preg_quote($name).'\s*?({|,)/', $this->string);
  280. }
  281. /**
  282. * Removes all instances of a particular property from the css string
  283. *
  284. * @author Anthony Short
  285. * @param $property string
  286. * @param $value string
  287. * @param $css string
  288. */
  289. public function remove_properties($property, $value)
  290. {
  291. return preg_replace('/'.$property.'\s*\:\s*'.$value.'\s*\;/', '', $this->string);
  292. }
  293. /**
  294. * Encodes or decodes parts of the css that break the xml
  295. *
  296. * @author Anthony Short
  297. * @param $css
  298. * @return string
  299. */
  300. public function convert_entities($action = 'encode', $css = false)
  301. {
  302. if($css === false)
  303. $css =& $this->string;
  304. $css_replacements = array(
  305. '"' => '#SCAFFOLD-QUOTE#',
  306. '>' => '#SCAFFOLD-GREATER#',
  307. '&' => '#SCAFFOLD-PARENT#',
  308. '?' => '#SCAFFOLD-NESTED-PARENT#',
  309. 'data:image/PNG;' => '#SCAFFOLD-IMGDATA-PNG#',
  310. 'data:image/JPG;' => "#SCAFFOLD-IMGDATA-JPG#",
  311. 'data:image/png;' => '#SCAFFOLD-IMGDATA-PNG#',
  312. 'data:image/jpg;' => "#SCAFFOLD-IMGDATA-JPG#",
  313. 'http://' => "#SCAFFOLD-HTTP#",
  314. );
  315. switch ($action)
  316. {
  317. case 'decode':
  318. $this->string = str_replace(array_values($css_replacements),array_keys($css_replacements), $this->string);
  319. break;
  320. case 'encode':
  321. $this->string = str_replace(array_keys($css_replacements),array_values($css_replacements), $this->string);
  322. break;
  323. }
  324. return $css;
  325. }
  326. }