PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/Scaffold/Helper/CSS.php

http://github.com/anthonyshort/Scaffold
PHP | 427 lines | 191 code | 63 blank | 173 comment | 6 complexity | 6cf9979ba241400b471ee566548d383b MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. /**
  3. * Scaffold_CSS
  4. *
  5. * Helper methods for dealing with CSS
  6. *
  7. * @package Scaffold
  8. * @author Anthony Short <anthonyshort@me.com>
  9. * @copyright 2009-2010 Anthony Short. All rights reserved.
  10. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  11. * @link https://github.com/anthonyshort/csscaffold/master
  12. */
  13. class Scaffold_Helper_CSS
  14. {
  15. /**
  16. * Valid start of a selector
  17. * @var string
  18. */
  19. public $selector_start = '[0-9A-Za-z\_\#\.\*\:\&\=]';
  20. /**
  21. * Regex for a valid selector
  22. * @var string
  23. */
  24. public $selector = '[0-9a-zA-Z\_\-\*\&\#\[\]\~\=\|\"\'\^\$\:\>\+\(\)\.\s\,]*';
  25. /**
  26. * Regex for a block
  27. * @var string
  28. */
  29. public $block = '\{([^}]*)\}';
  30. /**
  31. * Create a regex string from a string of pseudo-regex. Yep.
  32. * String variables that can be used:
  33. *
  34. * - IDENTIFIER
  35. * - BLOCK
  36. *
  37. * @access public
  38. * @param string
  39. * @return string
  40. */
  41. public function create_regex($str)
  42. {
  43. $str = str_replace(" ", '\s+', $str);
  44. $str = str_replace('IDENTIFIER', $this->selector_start . $this->selector, $str);
  45. $str = str_replace('BLOCK', $this->block, $str);
  46. return $str;
  47. }
  48. /**
  49. * Removes single-line comments from a string
  50. * @access public
  51. * @param string
  52. * @return string
  53. */
  54. public function remove_inline_comments($string)
  55. {
  56. return preg_replace('#($|\s)//.*$#Umsi', '', $string);
  57. }
  58. /**
  59. * Removes line breaks and tabs
  60. * @access public
  61. * @param $string
  62. * @return string
  63. */
  64. public function remove_newlines($string)
  65. {
  66. return preg_replace('/\n+|\r+|\t+/', '', $string);
  67. }
  68. /**
  69. * Removes css comments
  70. * @access public
  71. * @param $string
  72. * @return string
  73. */
  74. public function remove_comments($string)
  75. {
  76. return preg_replace('#/\*[^*]*\*+([^/*][^*]*\*+)*/#', '', $string);
  77. }
  78. /**
  79. * Encodes a selector so that it's able to be used in regular expressions
  80. * @access public
  81. * @param $selector
  82. * @return string
  83. */
  84. public function escape_regex($selector)
  85. {
  86. $selector = preg_quote($selector,'-');
  87. $selector = str_replace('#','\#',$selector);
  88. $selector = preg_replace('/\s+/',"\s+",$selector);
  89. return $selector;
  90. }
  91. // ============================
  92. // = Function Methods =
  93. // ============================
  94. /**
  95. * Finds CSS 'functions'. These are things like url(), embed() etc.
  96. * Handles interior brackets as well by using recursion.
  97. * Also handles nested functions.
  98. * @access public
  99. * @param $name
  100. * @param $string
  101. * @param $type int Which array to return
  102. * @return array
  103. */
  104. public function find_functions($name,$string)
  105. {
  106. $return = array();
  107. $regex ="/{$name}(\s*\(\s*((?:(?0)|(?1)|[^()]+)*)\s*\)\s*)/sx";
  108. if(preg_match_all($regex, $string, $match))
  109. {
  110. foreach($match[0] as $key => $value)
  111. {
  112. $params = $match[2][$key];
  113. // Encode commas in between braces so we can have nested functions
  114. if(preg_match_all('/\(.*?\,.*?\)/',$match[2][$key],$nested_commas))
  115. {
  116. foreach($nested_commas[0] as $key => $commas)
  117. {
  118. $replace = str_replace(',','#COMMA#',$commas);
  119. $params = str_replace($commas,$replace,$params);
  120. }
  121. }
  122. // Break the params into an array
  123. $params = explode(',',$params);
  124. // Decode commas
  125. foreach($params as $param_key => $param)
  126. {
  127. $params[$param_key] = str_replace('#COMMA#',',',$param);
  128. }
  129. $return[] = array(
  130. 'string' => $value,
  131. 'params' => $params
  132. );
  133. }
  134. }
  135. return $return;
  136. }
  137. // ============================
  138. // = Ruleset Methods =
  139. // ============================
  140. /**
  141. * Takes a string of a CSS rule:
  142. *
  143. * property:value;
  144. * property:value;
  145. * etc.
  146. *
  147. * And breaks it into an array:
  148. *
  149. * array('property'=>'value');
  150. *
  151. * @param $string
  152. * @return array
  153. */
  154. public function ruleset_to_array($string)
  155. {
  156. $return = array();
  157. foreach(explode(";", $string) as $value)
  158. {
  159. // Encode any colons inside quotations
  160. if(preg_match_all('/[\'"](.*?\:.*?)[\'"]/',$value,$m) )
  161. {
  162. $value = str_replace($m[0][0],str_replace(':','#COLON#',$m[0][0]),$value);
  163. }
  164. $value = explode(":", $value);
  165. // Make sure it's set
  166. if(isset($value[1]))
  167. {
  168. $return[trim($value[0])] = str_replace('#COLON#', ':', trim($value[1]));
  169. }
  170. }
  171. return $return;
  172. }
  173. // ============================
  174. // = Selector Methods =
  175. // ============================
  176. /**
  177. * Finds selectors which contain a particular property
  178. * @access public
  179. * @param $property string
  180. * @param $css string
  181. * @return array
  182. */
  183. public function find_selectors_with_property($property,$css)
  184. {
  185. $return = array();
  186. $regex = "/([^{}]*)\s*\{\s*($property|[^}]*[\s\;]$property)\s*:[^}]*}/sx";
  187. if(preg_match_all($regex,$css,$match))
  188. {
  189. foreach($match[0] as $key => $value)
  190. {
  191. $return[$key] = array(
  192. 'string' => $value,
  193. 'selector' => $match[1][$key]
  194. );
  195. }
  196. }
  197. return $return;
  198. }
  199. /**
  200. * Finds a selector and returns it as string
  201. * @access public
  202. * @param $selector string
  203. * @param $string string
  204. * @todo This will break if the selector they try and find is actually part of another selector
  205. */
  206. public function find_selectors($selector,$string,$escape = true)
  207. {
  208. $selector = ($escape) ? self::escape_regex($selector) : $selector;
  209. $regex = "/(^|[\}\s\;])* ( ($selector) \s*\{[^}]*\} )/sx";
  210. return preg_match_all($regex, $string, $match) ? $match[2] : array();
  211. }
  212. /**
  213. * Check if a selector exists
  214. * @param $name
  215. * @param $string
  216. * @return boolean
  217. */
  218. public function selector_exists($name,$string)
  219. {
  220. return preg_match('/(^|})\s*'.$name.'\s*\{/', $string) ? true : false;
  221. }
  222. /**
  223. * Checks if a selector is valid
  224. * @param $string
  225. * @return boolean
  226. */
  227. public function valid_selector($string)
  228. {
  229. return preg_match(self::$_identifier,$string);
  230. }
  231. // ============================
  232. // = Property Methods =
  233. // ============================
  234. /**
  235. * Finds all properties with a particular value
  236. * @access public
  237. * @param $property Regex formatted string for a property
  238. * @param $value Regex formatted string for a value
  239. * @param $css
  240. * @return array
  241. */
  242. public function find_properties_with_value($property,$value,$css,$escape_value=true)
  243. {
  244. $return = array();
  245. $escaped_property = self::escape_regex($property);
  246. $value = $escape_value ? self::escape_regex($value) : $value;
  247. $regex = "/
  248. \s*
  249. ([^{}]*) (?# selector)
  250. \s*
  251. \{
  252. (?: (?# property is complicated, as we may be first or intermediate - dbackground instance)
  253. [^}]*[\s\;]
  254. |
  255. \s*
  256. )
  257. ($escaped_property\s*)
  258. : (?# property value seperator)
  259. (\s*$value\s*;?)
  260. [^}]* (?# everything after our property of interest)
  261. \} (?# end content)
  262. /sx";
  263. if(preg_match_all($regex,$css,$matches,PREG_SET_ORDER))
  264. {
  265. foreach ( $matches as $key => $match )
  266. {
  267. $return[$key] = array(
  268. 'string' => trim($match[0]),
  269. 'selector' => $match[1],
  270. 'property' => $match[2].':'.$match[3],
  271. 'value' => trim($match[3],':; ')
  272. );
  273. }
  274. }
  275. return $return;
  276. }
  277. /**
  278. * Removes all instances of a particular property from the css string
  279. * @access public
  280. * @param $property string
  281. * @param $value string
  282. * @param $css string
  283. */
  284. public function remove_properties_with_value($property,$value,$string,$escape_value=true)
  285. {
  286. # Prepare
  287. $escaped_property = self::escape_regex($property);
  288. $value = $escape_value ? self::escape_regex($value) : $value;
  289. # Generate regex
  290. $regex = "/
  291. (
  292. \{
  293. (?: (?# property is complicated, as we may be first or intermediate - dbackground instance)
  294. \s*
  295. |
  296. [^}]*[\s\;]
  297. )
  298. )
  299. ($escaped_property\s*)
  300. : (?# property value seperator)
  301. (\s*$value\s*;?)
  302. /sx";
  303. # Remove property
  304. $string = preg_replace($regex, '$1', $string);
  305. # Return string
  306. return $string;
  307. }
  308. /**
  309. * Finds all properties within a css string
  310. * @access public
  311. * @param $property string Regex formatted string
  312. * @param $string string
  313. */
  314. public function find_properties($property,$string)
  315. {
  316. return self::find_properties_with_value($property,'[^;}]*',$string,false);
  317. }
  318. /**
  319. * Removes all instances of a particular property from the css string
  320. * @access public
  321. * @param $property string
  322. * @param $string string
  323. */
  324. public function remove_properties($property,$string)
  325. {
  326. return self::remove_properties_with_value($property,'[^;}]*',$string,false);
  327. }
  328. // ============================
  329. // = @ Rule Methods =
  330. // ============================
  331. /**
  332. * Finds @groups within the css and returns
  333. * an array with the values, and groups.
  334. * @access public
  335. * @param $name
  336. * @param $string
  337. * @return array
  338. */
  339. public function find_atrule($name,$string)
  340. {
  341. $name = self::escape_regex($name);
  342. $regex = "/
  343. @{$name} (?# the name to find)
  344. ([^{]*?) (?# atrules params)
  345. \{ (?# start atrule content)
  346. (.*?)
  347. \} (?# end atrule content)
  348. /xs";
  349. $result = preg_match_all($regex, $string, $matches, PREG_SET_ORDER) ? $matches : array();
  350. return $result;
  351. }
  352. /**
  353. * Removes an atrule from a CSS string
  354. * @access public
  355. * @param $name
  356. * @param $css
  357. * @return string
  358. */
  359. public function remove_atrule($name,$css)
  360. {
  361. $rules = self::find_atrule($name,$css);
  362. return str_replace($rules[0],'',$css);
  363. }
  364. }