Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- /**
- * CSS 文件差异比较工具
- */
- class CssCompare
- {
- const NAME = 0;
- const SCHEME = 1;
- const VALUE = 2;
- const LEFT = 'left';
- const RIGHT = 'right';
- const DIFF = 'diff';
- /**
- * @var object
- */
- private static $block = null;
- /**
- * @var array
- */
- private static $diff = [];
- /**
- * 比较代码片段
- * @param string $a 代码片段A
- * @param string $b 代码片段B
- * @return array
- */
- public static function diff($a, $b)
- {
- return static::compare(static::parse($a), static::parse($b));
- }
- /**
- * @param $a
- * @param $b
- * @return array
- */
- protected static function compare($a, $b)
- {
- static::$diff = [];
- $schemeA = $a['scheme'];
- $schemeB = $b['scheme'];
- foreach ((array) $schemeA as $key => $val) {
- if (isset($schemeB[$key])) {
- if ($val !== $schemeB[$key]) {
- static::$diff['_@'.$key] = [static::DIFF, $val, $schemeB[$key]];
- }
- unset($schemeB[$key]);
- } else {
- static::$diff['_@'.$key] = [static::LEFT, $val, null];
- }
- }
- foreach ((array) $schemeB as $key => $val) {
- static::$diff['_@'.$key] = [static::RIGHT, null, $val];
- }
- static::compareRules($a['rules'], $b['rules']);
- return static::$diff;
- }
- /**
- * @param $a
- * @param $b
- * @param string $parent
- */
- protected static function compareRules($a, $b, $parent = '')
- {
- $childA = $a->children;
- $childB = $b->children;
- foreach ($childA as $key => $rule) {
- $name = ($parent === '' ? '' : $parent.' >> ').$key;
- if (!isset($childB[$key])) {
- static::$diff[$name] = [static::LEFT, is_object($rule) ? $rule->children : $rule, null];
- } else {
- if (is_object($rule)) {
- if (!is_object($childB[$key])) {
- static::$diff[$name] = [static::DIFF, $rule->children, $childB[$key]];
- } else {
- static::compareRules($rule, $childB[$key], $name);
- }
- } elseif ($rule !== $childB[$key]) {
- static::$diff[$name] = [static::DIFF, $rule, $childB[$key]];
- }
- unset($childB[$key]);
- }
- }
- foreach ($childB as $key => $rule) {
- $name = ($parent === '' ? '' : ' >> ').$key;
- static::$diff[$name] = [static::RIGHT, null, $rule];
- }
- }
- /**
- * @param $source
- * @return array
- */
- protected static function parse($source)
- {
- $scheme = [];
- $source = preg_replace_callback('#@(\w+)([^;}{]+);#', function($match) use (&$scheme) {
- $key = trim($match[1]);
- $scheme[$key] = trim($match[2]);
- return '';
- }, $source);
- ksort($scheme);
- return [
- 'scheme' => $scheme,
- 'rules' => static::rules($source)
- ];
- }
- /**
- * @param $source
- * @return object
- */
- protected static function rules($source)
- {
- static::$block = null;
- static::pushBlock();
- $source = static::removeComments($source);
- $current = self::NAME;
- $len = strlen($source);
- $i = 0;
- $deep = 0;
- $scheme = '';
- $name = '';
- $value = '';
- while ($i < $len) {
- $buffer = $source[$i];
- $continue = false;
- if (strcasecmp($buffer, '@') === 0) {
- $scheme = '';
- $continue = true;
- $current = self::SCHEME;
- } elseif (strcasecmp($buffer, '{') === 0) {
- if ($current === self::SCHEME) {
- // block start
- $deep++;
- $current = self::NAME;
- static::pushBlock('@'.preg_replace('!\s+!', ' ', trim($scheme)));
- } else {
- // rule start
- $current = self::VALUE;
- }
- $continue = true;
- } elseif (strcasecmp($buffer, '}') === 0) {
- if ($deep && $current === self::NAME) {
- // block end
- $deep--;
- $scheme = '';
- static::popBlock();
- } else {
- // rule end
- $items = [];
- $defines = array_filter(explode(';', trim($value)));
- foreach ($defines as $define) {
- $define = trim($define);
- $kv = explode(':', $define, 2);
- if (count($kv) > 1) {
- $key = trim($kv[0]);
- $items[$key] = preg_replace('/\s+/', '', $kv[1]);
- }
- }
- $currentRules = static::$block->children;
- $name = preg_replace('!\s+!', ' ', trim($name));
- $names = explode(',', $name);
- foreach ($names as $name) {
- $name = trim($name);
- $newItem = isset($currentRules[$name]) ? array_merge($currentRules[$name], $items) : $items;
- ksort($newItem);
- $currentRules[$name] = $newItem;
- }
- static::$block->children = $currentRules;
- $name = $value = '';
- $current = self::NAME;
- }
- $continue = true;
- }
- if (!$continue) {
- if ($current === self::SCHEME) {
- $scheme .= $buffer;
- } elseif ($current === self::NAME) {
- $name .= $buffer;
- }else {
- $value .= $buffer;
- }
- }
- $i++;
- }
- return static::$block;
- }
- /**
- * @param null $name
- * @return object
- */
- protected static function pushBlock($name = null)
- {
- $block = (object) ['children' => [], 'parent' => null];
- if (static::$block) {
- if ($name) {
- static::$block->children[$name] = $block;
- }
- $block->parent = static::$block;
- }
- return static::$block = $block;
- }
- /**
- * @return void
- */
- protected static function popBlock()
- {
- if (static::$block && static::$block->parent) {
- static::$block = static::$block->parent;
- }
- }
- /**
- * @param string $buffer
- * @return string
- */
- protected static function removeComments($buffer)
- {
- $regex = [
- "`^([\t\s]+)`ism" => '',
- "`^\/\*(.+?)\*\/`ism" => "",
- "`([\n\A;]+)\/\*(.+?)\*\/`ism" => "$1",
- "`([\n\A;\s]+)//(.+?)[\n\r]`ism" => "$1\n",
- "`(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+`ism" => "\n"
- ];
- return preg_replace(array_keys($regex), $regex, $buffer);
- }
- }
Add Comment
Please, Sign In to add comment