Guest User

Untitled

a guest
Aug 18th, 2018
76
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.05 KB | None | 0 0
  1. <?php
  2.  
  3. /**
  4. * CSS 文件差异比较工具
  5. */
  6. class CssCompare
  7. {
  8. const NAME = 0;
  9. const SCHEME = 1;
  10. const VALUE = 2;
  11.  
  12. const LEFT = 'left';
  13. const RIGHT = 'right';
  14. const DIFF = 'diff';
  15.  
  16. /**
  17. * @var object
  18. */
  19. private static $block = null;
  20.  
  21. /**
  22. * @var array
  23. */
  24. private static $diff = [];
  25.  
  26. /**
  27. * 比较代码片段
  28. * @param string $a 代码片段A
  29. * @param string $b 代码片段B
  30. * @return array
  31. */
  32. public static function diff($a, $b)
  33. {
  34. return static::compare(static::parse($a), static::parse($b));
  35. }
  36.  
  37. /**
  38. * @param $a
  39. * @param $b
  40. * @return array
  41. */
  42. protected static function compare($a, $b)
  43. {
  44. static::$diff = [];
  45. $schemeA = $a['scheme'];
  46. $schemeB = $b['scheme'];
  47. foreach ((array) $schemeA as $key => $val) {
  48. if (isset($schemeB[$key])) {
  49. if ($val !== $schemeB[$key]) {
  50. static::$diff['_@'.$key] = [static::DIFF, $val, $schemeB[$key]];
  51. }
  52. unset($schemeB[$key]);
  53. } else {
  54. static::$diff['_@'.$key] = [static::LEFT, $val, null];
  55. }
  56. }
  57. foreach ((array) $schemeB as $key => $val) {
  58. static::$diff['_@'.$key] = [static::RIGHT, null, $val];
  59. }
  60. static::compareRules($a['rules'], $b['rules']);
  61. return static::$diff;
  62. }
  63.  
  64. /**
  65. * @param $a
  66. * @param $b
  67. * @param string $parent
  68. */
  69. protected static function compareRules($a, $b, $parent = '')
  70. {
  71. $childA = $a->children;
  72. $childB = $b->children;
  73. foreach ($childA as $key => $rule) {
  74. $name = ($parent === '' ? '' : $parent.' >> ').$key;
  75. if (!isset($childB[$key])) {
  76. static::$diff[$name] = [static::LEFT, is_object($rule) ? $rule->children : $rule, null];
  77. } else {
  78. if (is_object($rule)) {
  79. if (!is_object($childB[$key])) {
  80. static::$diff[$name] = [static::DIFF, $rule->children, $childB[$key]];
  81. } else {
  82. static::compareRules($rule, $childB[$key], $name);
  83. }
  84. } elseif ($rule !== $childB[$key]) {
  85. static::$diff[$name] = [static::DIFF, $rule, $childB[$key]];
  86. }
  87. unset($childB[$key]);
  88. }
  89. }
  90. foreach ($childB as $key => $rule) {
  91. $name = ($parent === '' ? '' : ' >> ').$key;
  92. static::$diff[$name] = [static::RIGHT, null, $rule];
  93. }
  94. }
  95.  
  96. /**
  97. * @param $source
  98. * @return array
  99. */
  100. protected static function parse($source)
  101. {
  102. $scheme = [];
  103. $source = preg_replace_callback('#@(\w+)([^;}{]+);#', function($match) use (&$scheme) {
  104. $key = trim($match[1]);
  105. $scheme[$key] = trim($match[2]);
  106. return '';
  107. }, $source);
  108. ksort($scheme);
  109. return [
  110. 'scheme' => $scheme,
  111. 'rules' => static::rules($source)
  112. ];
  113. }
  114.  
  115. /**
  116. * @param $source
  117. * @return object
  118. */
  119. protected static function rules($source)
  120. {
  121. static::$block = null;
  122. static::pushBlock();
  123. $source = static::removeComments($source);
  124. $current = self::NAME;
  125. $len = strlen($source);
  126.  
  127. $i = 0;
  128. $deep = 0;
  129. $scheme = '';
  130. $name = '';
  131. $value = '';
  132.  
  133. while ($i < $len) {
  134. $buffer = $source[$i];
  135. $continue = false;
  136. if (strcasecmp($buffer, '@') === 0) {
  137.  
  138. $scheme = '';
  139. $continue = true;
  140. $current = self::SCHEME;
  141.  
  142. } elseif (strcasecmp($buffer, '{') === 0) {
  143.  
  144. if ($current === self::SCHEME) {
  145. // block start
  146. $deep++;
  147. $current = self::NAME;
  148. static::pushBlock('@'.preg_replace('!\s+!', ' ', trim($scheme)));
  149. } else {
  150. // rule start
  151. $current = self::VALUE;
  152. }
  153. $continue = true;
  154.  
  155. } elseif (strcasecmp($buffer, '}') === 0) {
  156.  
  157. if ($deep && $current === self::NAME) {
  158. // block end
  159. $deep--;
  160. $scheme = '';
  161. static::popBlock();
  162. } else {
  163. // rule end
  164. $items = [];
  165. $defines = array_filter(explode(';', trim($value)));
  166. foreach ($defines as $define) {
  167. $define = trim($define);
  168. $kv = explode(':', $define, 2);
  169. if (count($kv) > 1) {
  170. $key = trim($kv[0]);
  171. $items[$key] = preg_replace('/\s+/', '', $kv[1]);
  172. }
  173. }
  174. $currentRules = static::$block->children;
  175.  
  176. $name = preg_replace('!\s+!', ' ', trim($name));
  177. $names = explode(',', $name);
  178. foreach ($names as $name) {
  179. $name = trim($name);
  180. $newItem = isset($currentRules[$name]) ? array_merge($currentRules[$name], $items) : $items;
  181. ksort($newItem);
  182. $currentRules[$name] = $newItem;
  183. }
  184. static::$block->children = $currentRules;
  185.  
  186. $name = $value = '';
  187. $current = self::NAME;
  188. }
  189. $continue = true;
  190.  
  191. }
  192. if (!$continue) {
  193. if ($current === self::SCHEME) {
  194. $scheme .= $buffer;
  195. } elseif ($current === self::NAME) {
  196. $name .= $buffer;
  197. }else {
  198. $value .= $buffer;
  199. }
  200. }
  201. $i++;
  202. }
  203. return static::$block;
  204. }
  205.  
  206. /**
  207. * @param null $name
  208. * @return object
  209. */
  210. protected static function pushBlock($name = null)
  211. {
  212. $block = (object) ['children' => [], 'parent' => null];
  213. if (static::$block) {
  214. if ($name) {
  215. static::$block->children[$name] = $block;
  216. }
  217. $block->parent = static::$block;
  218. }
  219. return static::$block = $block;
  220. }
  221.  
  222. /**
  223. * @return void
  224. */
  225. protected static function popBlock()
  226. {
  227. if (static::$block && static::$block->parent) {
  228. static::$block = static::$block->parent;
  229. }
  230. }
  231.  
  232. /**
  233. * @param string $buffer
  234. * @return string
  235. */
  236. protected static function removeComments($buffer)
  237. {
  238. $regex = [
  239. "`^([\t\s]+)`ism" => '',
  240. "`^\/\*(.+?)\*\/`ism" => "",
  241. "`([\n\A;]+)\/\*(.+?)\*\/`ism" => "$1",
  242. "`([\n\A;\s]+)//(.+?)[\n\r]`ism" => "$1\n",
  243. "`(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+`ism" => "\n"
  244. ];
  245. return preg_replace(array_keys($regex), $regex, $buffer);
  246. }
  247. }
Add Comment
Please, Sign In to add comment