Location: PHPKode > scripts > CSS Compressor > css-compressor/css-compression.php
<?
/**
 * CSS Compressor
 * r:4 - April 28, 2009
 * Corey Hart @ http://www.codenothing.com
 */ 

// Include miscellaneous variables
include("vars/collect.php");


Class CSSCompression
{
	var $selectors = array();
	var $details = array();
	var $options = array();

	function setOptions(){
		// Converts long color names to short hex names (aliceblue -> #f0f8ff)
		$this->options['color-long2hex'] = $_POST['color-long2hex'];

		// Converts rgb colors to hex (rgb(159,80,98) -> #9F5062)
		$this->options['color-rgb2hex'] = $_POST['color-rgb2hex'];

		// Converts long hex codes to short color names (#f5f5dc -> beige)
		$this->options['color-hex2shortcolor'] = $_POST['color-hex2shortcolor'];

		// Converts long hex codes to short hex codes (#44ff11 -> #4f1)
		$this->options['color-hex2shorthex'] = $_POST['color-hex2shorthex'];

		// Converts font-weight names to numbers (bold -> 700)
		$this->options['fontweight2num'] = $_POST['fontweight2num'];

		// Removes zero decimals and 0 units (15.0px -> 15px || 0px -> 0)
		$this->options['format-units'] = $_POST['format-units'];

		// Lowercases html tags from list (BODY -> body)
		$this->options['lowercase-selectors'] = $_POST['lowercase-selectors'];

		// Compresses single defined multi-directional properties (margin: 15px 25px 15px 25px -> margin:15px 25px)
		$this->options['directional-compress'] = $_POST['directional-compress'];

		// Combines multiply defined selectors (p{color:blue;} p{font-size:12pt} -> p{color:blue;font-size:12pt;})
		$this->options['multiple-selectors'] = $_POST['multiple-selectors'];

		// Combines selectors with same details (p{color:blue;} a{color:blue;} -> p,a{color:blue;})
		$this->options['multiple-details'] = $_POST['multiple-details'];

		// Combines color/style/width properties (border-style:dashed;border-color:black;border-width:4px; -> border:4px dashed black)
		$this->options['csw-combine'] = $_POST['csw-combine'];

		// Combines margin/padding directionals (margin-top:10px;margin-right:5px;margin-bottom:4px;margin-left:1px; -> margin:10px 5px 4px 1px;)
		$this->options['mp-combine'] = $_POST['mp-combine'];

		// Combines border directionals (border-top|right|bottom|left:1px solid black -> border:1px solid black)
		$this->options['border-combine'] = $_POST['border-combine'];

		// Combines font properties (font-size:12pt; font-family: arial; -> font:12pt arial)
		$this->options['font-combine'] = $_POST['font-combine'];

		// Combines background properties (background-color: black; background-image: url(bgimg.jpeg); -> background:black url(bgimg.jpeg))
		$this->options['background-combine'] = $_POST['background-combine'];

		// Combines list-style properties (list-style-type: round; list-style-position: outside -> list-style:round outside)
		$this->options['list-combine'] = $_POST['list-combine'];

		// Removes multiply defined properties
		// STRONGLY SUGGESTED TO KEEP THIS TRUE
		$this->options['rm-multi-define'] = $_POST['rm-multi-define'];
	}

	function compress($css){
		// Use defined options
		$this->setOptions();
		// Send body through initial trimings
		$css = $this->initialTrim($css);

		// Seperate the element from the elements details
		$css = explode("\n", $css);
		foreach ($css as $details){
			// Determine whether your looking at the details or element
			if (ereg("^{", $details)){
				unset($storage);
				$details = substr($details, 1, strlen($details)-2);
				$details = explode(";", $details);
				foreach($details as $line){
					if (eregi("^(url|@import)", $line)){
						$storage .= $line.";";
						continue;
					}
					list ($property, $value) = explode(":", $line);
					// Fail safe, remove unknown tag/elements
					if (!isset($property) || !isset($value)) continue;

					// Run the tag/element through each compression
					list ($property, $value) = $this->runSpecialCompressions($property, $value);

					// Store the compressed element
					$storage .= "$property:$value;";
				}
				$this->details[count($this->details)] = $storage;
			}
			else if (eregi("^@import", trim($details))){
				list ($import, $details) = explode(";", $details);
				$IMPORT_STR .= trim($import).";";
				$this->selectors[count($this->selectors)] = trim($details);
			}
			else if ($details){
				$this->selectors[count($this->selectors)] = trim($details);
			}

		}
		// Compression Functions
		if ($this->options['lowercase-selectors']) $this->lowercaseSelectors();
		if ($this->options['multiple-selectors']) $this->combineMultiplyDefinedSelectors();
		if ($this->options['multiple-details']) $this->combineMuliplyDefinedDetails();
		if ($this->options['csw-combine']) $this->combineCSWproperties();
		if ($this->options['mp-combine']) $this->combineMPproperties();
		if ($this->options['border-combine']) $this->combineBorderDefinitions();
		if ($this->options['font-combine']) $this->combineFontDefinitions();
		if ($this->options['background-combine']) $this->combineBackgroundDefinitions();
		if ($this->options['list-combine']) $this->combineListProperties();
		if ($this->options['rm-multi-define']) $this->removeMultipleDefinitions();

		// Store compressed script into body var
		$css = $IMPORT_STR;
		foreach ($this->selectors as $k=>$v){
			$css .= trim("$v{".$this->details[$k]."}");
		}

		// Return compressed css
		return $css;
	}

	function initialTrim($css){
		// Regex
		$search = array(
			// 0-4 remove spaces/newline/returns/etc.
			1 => "(\r|\n|\t)is", // Move extraneous spaces
			2 => "(\s{2,})is", // Remove multiple spaces
			3 => "((\/\*|\<\!\-\-)(.*?)(\*\/|\-\-\>))is", // Remove all comments
			4 => "(\s{0,}([,{};:>])\s{0,})is", // Remove un-needed spaces around special characters
			5 => "(url\(['\"](.*?)['\"]\))is", // Remove quotes from urls
			6 => "(;{2,})is", // Remove unecessary semi-colons
			// Leave section open for additional entries

			// Break apart elements for setup of further compression
			20 => "(\{)is",
			21 => "(\})is",
		);

		// Replacements
		$replace = array(
			1 => " ",
			2 => " ",
			3 => " ",
			4 => "$1",
			5 => "url($1)",
			6 => ";",
			// Leave section open for additional entries

			// Add new line for setup of further compression
			20 => "\n{",
			21 => "}\n",
		);

		// Run replacements
		return preg_replace($search, $replace, $css);
	}

	function runSpecialCompressions($prop, $val){
		global $fontweight2num;
		// Properties should always be lowercase
		$prop = strtolower($prop);

		// Remove uneeded side definitions if possible
		if ($this->options['directional-compress'] && eregi("^(margin|padding|border-width)", $prop)){
			$val = $this->sidesDirectional($val);
		}

		// Font-weight converter
		if ($this->options['fontweight2num'] && eregi("^font-weight$", $prop)){
			if (isset($fontweight2num[strtolower($val)]))
				$val = $fontweight2num[strtolower($val)];
		}

		// Remove uneeded decimals/units
		if ($this->options['format-units']){
			$val = $this->removeDecimal($val);
			$val = $this->removeUnits($val);
		}

		// Only run on color changes on color definitions
		// (Working on recognizing colors from names)
		if (eregi("color", $prop)){
			$val = $this->runColorChanges($val);
		}

		// Return for list retrival
		return array($prop, $val);
	}

	function sidesDirectional($val){
		// Side definitions already reduced down to a single definition
		if (!ereg(" ", $val)){
			// Redundent, but just in case
			if ($this->options['format-units']){
				$val = $this->removeDecimal($val);
				$val = $this->removeUnits($val);
			}
			return $val;
		}

		// Split up each definiton
		$direction = explode(" ", $val);

		// Zero out and remove units if possible
		if ($this->options['format-units']){
			foreach ($direction as $k=>$v) $direction[$k] = $this->removeDecimal($v);
			foreach ($direction as $k=>$v) $direction[$k] = $this->removeUnits($v);
		}

		// 4 Direction reduction
		if (count($direction) == 4){
			if ($direction[0] == $direction[1] && $direction[2] == $direction[3] && $direction[0] == $direction[3]){
				// All 4 sides are the same, combine into 1 definition
				$val = $direction[0];
			}
			else if ($direction[0] == $direction[2] && $direction[1] == $direction[3]){
				// top-bottom/left-right are the same, reduce definition
				$val = $direction[0]." ".$direction[1];
			}
			else{
				// No reduction found, return in initial form
				$val = implode(" ", $direction);
			}
		}
		// 2 Direction reduction
		else if (count($direction) == 2){
			if ($direction[0] == $direction[1]){
				// Both directions are the same, combine into single definition
				$val = $direction[0];
			}else{
				// No reduction found, return in initial form
				$val = implode(" ", $direction);
			}
		}
		// No reduction found, return in initial form
		else{
			$val = implode(" ", $direction);
		}

		// Return the value of the property
		return $val;
	}

	function removeDecimal($str){
		// Find all instances of .0 and remove them
		$pattern = "/^(\d+\.0)(\w{0,2})/i";
		preg_match_all($pattern, $str, $matches);
		for ($i=0; $i<count($matches[1]); $i++){
			$search = "(".$matches[0][$i].")is";
			$replace = intval($matches[1][$i]).$matches[2][$i];
			$str = preg_replace($search, $replace, $str);
		}
		return $str;
	}

	function removeUnits($str){
		// Find all instants of 0 size and remove suffix
		$pattern = "/^(\d)(\w{0,2})/i";
		preg_match_all($pattern, $str, $matches);
		for ($i=0; $i<count($matches[1]); $i++){
			if (intval($matches[1][$i]) == 0){
				$search = "(".$matches[0][$i].")is";
				$replace = "0";
				$str = preg_replace($search, $replace, $str);
			}
		}
		return $str;
	}

	function runColorChanges($val){
		global $long2hex, $hex2short;
		// Set up search and replace arrays
		$convert['search'] = array();
		$convert['replace'] = array();
		// Push long2hex vals for first conversion
		if ($this->options['color-long2hex']){
			foreach ($long2hex as $x=>$y){
				array_push($convert['search'], "($x)is");
				array_push($convert['replace'], $y);
			}
		}
		// Now add hex2short vals
		if ($this->options['color-hex2shortcolor']){
			foreach($hex2short as $x=>$y){
				array_push($convert['search'], "($x)is");
				array_push($convert['replace'], $y);
			}
		}

		// Transfer rgb colors to hex codes
		if ($this->options['color-rgb2hex']){
			$pattern = "/rgb\((\d{1,3}(,\s?\d{1,3},\s?\d{1,3})?)\)/i";
			preg_match_all($pattern, $val, $matches);
			for ($i=0; $i<count($matches[1]); $i++){
				unset($new, $str);
				$hex = "0123456789ABCDEF";
				$str = explode(",", $matches[1][$i]);
				// Incase rgb was defined with single val
				if (!$str) $str = array($matches[1][$i]);
				foreach($str as $x){
					$x = intval($x);
					if ($x > 255) $x = 255;
					if ($x < 0) $x = 0;
					$new .= $hex[($x-$x%16)/16];
					$new .= $hex[$x%16];
				}
				// Repeat hex code to complete 6 digit hex requirement for single definitions
				if (count($str) == 1) $new .= $new.$new;
				// Escape out brackets
				$matches[0][$i] = str_replace("(", "\(", $matches[0][$i]);
				$matches[0][$i] = str_replace(")", "\)", $matches[0][$i]);
				// Replace within string
				$val = preg_replace("(".$matches[0][$i].")is", "#$new", $val);
			}
		}

		// Convert large hex codes to small codes
		if ($this->options['color-hex2shorthex']){
			$pattern = "/#([0-9a-fA-F]{6})/i";
			preg_match_all($pattern, $val, $matches);
			for ($i=0; $i<count($matches[1]); $i++){
				$hex = str_split($matches[1][$i], 1);
				if ($hex[0] == $hex[1] && $hex[2] == $hex[3] && $hex[4] == $hex[5]){
					$search = "(".$matches[0][$i].")is";
					$replace = "#".$hex[0].$hex[2].$hex[4];
					$val = preg_replace($search, $replace, $val);
				}
			}
		}

		// Run long2hex & hex2short conversions stored at start of function
		return preg_replace($convert['search'], $convert['replace'], $val);
	}

	function lowercaseSelectors(){
		global $standard_selectors;
		$search = array();
		$replace = array();
		foreach ($standard_selectors as $k=>$v){
			// Ensure its a tag thats being selected
			array_push($search, "(([^a-zA-Z0-9#.])$v)is");
			array_push($replace, "$1$v");
			// For when the tag is the first defined
			array_push($search, "(^$v)is");
			array_push($replace, "$v");
		}

		foreach ($this->selectors as $k=>$v){
			$this->selectors[$k] = preg_replace($search, $replace, $v);
		}
	}

	function combineMultiplyDefinedSelectors(){
		$max = array_pop(array_keys($this->selectors))+1;
		for ($i=0; $i<$max; $i++){
			if (!$this->selectors[$i]) continue;
			for ($k=$i+1; $k<$max; $k++){
				if (!$this->selectors[$k]) continue;
				if ($this->selectors[$i] == $this->selectors[$k]){
					$this->details[$i] .= $this->details[$k];
					unset($this->selectors[$k], $this->details[$k]);
				}
			}
		}
	}

	function combineMuliplyDefinedDetails(){
		$max = array_pop(array_keys($this->selectors))+1;
		for ($i=0; $i<$max; $i++){
			if (!$this->selectors[$i]) continue;
			$arr = explode(";", $this->details[$i]);
			for ($k=$i+1; $k<count($this->details); $k++){
				if (!$this->selectors[$k]) continue;
				$match = explode(";", $this->details[$k]);
				$x = array_diff($arr, $match);
				$y = array_diff($match, $arr);
				if (count($x) < 1 && count($y) < 1){
					$this->selectors[$i] .= ",".$this->selectors[$k];
					unset($this->details[$k], $this->selectors[$k]);
				}
			}
		}
	}

	function combineCSWproperties(){
		foreach ($this->details as $k=>$v){
			$storage = array();
			$pattern = "/(border|outline)-(color|style|width):(.*?);/i";
			preg_match_all($pattern, $this->details[$k], $matches);
			for ($i=0; $i<count($matches[1]); $i++){
				if (!isset($storage[$matches[1][$i]])) $storage[$matches[1][$i]] = array($matches[2][$i] => $matches[3][$i]);
				// Override double written properties
				$storage[$matches[1][$i]][$matches[2][$i]] = $matches[3][$i];
			}

			// Go through each tag for possible combination
			foreach($storage as $tag => $arr){
				if (count($arr) == 3){
					// String to replace each instance with
					$replace = "$tag:".$arr['width']." ".$arr['style']." ".$arr['color'];
					foreach ($arr as $x=>$y){
						// Replace every instance, as multiple declarations removal will correct it
						$search = "($tag-$x:$y)is";
						$this->details[$k] = preg_replace($search, $replace, $this->details[$k]);
					}
				}
			}
		}
	}

	function combineMPproperties(){
		foreach ($this->details as $k=>$v){
			$storage = array();
			$pattern = "/(margin|padding)-(top|right|bottom|left):(.*?);/i";
			preg_match_all($pattern, $this->details[$k], $matches);
			for ($i=0; $i<count($matches[1]); $i++){
				if (!isset($storage[$matches[1][$i]])) $storage[$matches[1][$i]] = array($matches[2][$i] => $matches[3][$i]);
				// Override double written properties
				$storage[$matches[1][$i]][$matches[2][$i]] = $matches[3][$i];
			}

			// Go through each tag for possible combination
			foreach($storage as $tag => $arr){
				// Drop capitols
				$tag = strtolower($tag);
				// Only combine if all 4 definitions are found
				if (count($arr) == 4){
					// If all definitions are the same, combine into single definition
					if ($arr['top'] == $arr['bottom'] && $arr['left'] == $arr['right'] && $arr['top'] == $arr['left']){
						// String to replace each instance with
						$replace = "$tag:".$arr['top'];
						foreach ($arr as $a=>$b){
							// Replace every instance, as multiple declarations removal will correct it
							$search = "($tag-$a:$b)is";
							$this->details[$k] = preg_replace($search, $replace, $this->details[$k]);
						}
					}
					// If opposites are the same, combine into single definition
					else if ($arr['top'] == $arr['bottom'] && $arr['left'] == $arr['right']){
						// String to replace each instance with
						$replace = "$tag:".$arr['top']." ".$arr['left'];
						foreach ($arr as $a=>$b){
							// Replace every instance, as multiple declarations removal will correct it
							$search = "($tag-$a:$b)is";
							$this->details[$k] = preg_replace($search, $replace, $this->details[$k]);
						}
					}
					else{
						// String to replace each instance with
						$replace = "$tag:".$arr['top']." ".$arr['right']." ".$arr['bottom']." ".$arr['left'];
						foreach ($arr as $a=>$b){
							// Replace every instance, as multiple declarations removal will correct it
							$search = "($tag-$a:$b)is";
							$this->details[$k] = preg_replace($search, $replace, $this->details[$k]);
						}
					}
				}
			}
		}
	}

	function combineBorderDefinitions(){
		foreach ($this->details as $k=>$v){
			$storage = array();
			$pattern = "/(border)-(top|right|bottom|left):(.*?);/i";
			preg_match_all($pattern, $this->details[$k], $matches);
			for ($i=0; $i<count($matches[1]); $i++){
				if (!isset($storage[$matches[1][$i]])) $storage[$matches[1][$i]] = array($matches[2][$i] => $matches[3][$i]);
				// Override double written properties
				$storage[$matches[1][$i]][$matches[2][$i]] = $matches[3][$i];
			}

			foreach ($storage as $tag => $arr){
				if (count($arr) == 4 && $arr['top'] == $arr['bottom'] && $arr['left'] == $arr['right'] && $arr['top'] == $arr['right']){
					// String to replace each instance with
					$replace = "$tag:".$arr['top'];
					foreach ($arr as $a=>$b){
						// Replace every instance, as multiple declarations removal will correct it
						$search = "($tag-$a:$b)is";
						$this->details[$k] = preg_replace($search, $replace, $this->details[$k]);
					}
				}
			}
		}
	}

	function combineFontDefinitions(){
		foreach ($this->details as $k=>$v){
			unset($replace, $storage);
			$pattern = "/(font|line)-(style|variant|weight|size|height|family):(.*?);/i";
			preg_match_all($pattern, $this->details[$k], $matches);
			for ($i=0; $i<count($matches[1]); $i++){
				// Store each property in it's full state
				$storage[$matches[1][$i]."-".$matches[2][$i]] = $matches[3][$i];
			}

			// Combine font-size & line-height if possible
			if (isset($storage['font-size']) && isset($storage['line-height'])){
				$storage['size/height'] = $storage['font-size']."/".$storage['line-height'];
				unset($storage['font-size'], $storage['line-height']);
			}

			// Run font checks and get replacement str
			$replace = $this->searchDefinitions("font", $storage, array("font-style", "font-variant", "font-weight", "size/height", "font-family"));
			if (!$replace) $replace = $this->searchDefinitions("font", $storage, array("font-style", "font-variant", "font-weight", "font-size", "font-family"));
			if (!$replace) $replace = $this->searchDefinitions("font", $storage, array("font-style", "font-variant", "size/height", "font-family"));
			if (!$replace) $replace = $this->searchDefinitions("font", $storage, array("font-style", "font-variant", "font-size", "font-family"));
			if (!$replace) $replace = $this->searchDefinitions("font", $storage, array("font-style", "font-weight", "size/height", "font-family"));
			if (!$replace) $replace = $this->searchDefinitions("font", $storage, array("font-style", "font-weight", "font-size", "font-family"));
			if (!$replace) $replace = $this->searchDefinitions("font", $storage, array("font-variant", "font-weight", "size/height", "font-family"));
			if (!$replace) $replace = $this->searchDefinitions("font", $storage, array("font-variant", "font-weight", "font-size", "font-family"));
			if (!$replace) $replace = $this->searchDefinitions("font", $storage, array("font-weight", "size/height", "font-family"));
			if (!$replace) $replace = $this->searchDefinitions("font", $storage, array("font-weight", "font-size", "font-family"));
			if (!$replace) $replace = $this->searchDefinitions("font", $storage, array("font-variant", "size/height", "font-family"));
			if (!$replace) $replace = $this->searchDefinitions("font", $storage, array("font-variant", "font-size", "font-family"));
			if (!$replace) $replace = $this->searchDefinitions("font", $storage, array("font-style", "size/height", "font-family"));
			if (!$replace) $replace = $this->searchDefinitions("font", $storage, array("font-style", "font-size", "font-family"));
			if (!$replace) $replace = $this->searchDefinitions("font", $storage, array("size/height", "font-family"));
			if (!$replace) $replace = $this->searchDefinitions("font", $storage, array("font-size", "font-family"));

			// If replacement string found, run it on all options
			if ($replace){
				for ($i=0; $i<count($matches[1]); $i++){
					if (!isset($storage['line-height']) || (isset($storage['line-height']) && !eregi("^line-height", $matches[0][$i]))){
						$v = preg_replace("(".$matches[0][$i].")is", $replace, $v);
					}
				}
			}

			// Replace details
			$this->details[$k] = $v;
		}
	}

	function combineBackgroundDefinitions(){
		foreach ($this->details as $k=>$v){
			unset($replace, $storage);
			$pattern = "/background-(color|image|repeat|attachment|position):(.*?);/i";
			preg_match_all($pattern, $this->details[$k], $matches);
			for ($i=0; $i<count($matches[1]); $i++){
				// Store each property in it's full state
				$storage[$matches[1][$i]] = $matches[2][$i];
			}

			// Run background checks and get replacement str
			// With color
			$replace = $this->searchDefinitions("background", $storage, array("color", "image", "repeat", "attachment", "position"));
			if (!$replace) $replace = $this->searchDefinitions("background", $storage, array("color", "image", "attachment", "position"));
			if (!$replace) $replace = $this->searchDefinitions("background", $storage, array("color", "image", "repeat", "position"));
			if (!$replace) $replace = $this->searchDefinitions("background", $storage, array("color", "image", "repeat", "attachment"));
			if (!$replace) $replace = $this->searchDefinitions("background", $storage, array("color", "image", "repeat"));
			if (!$replace) $replace = $this->searchDefinitions("background", $storage, array("color", "image", "attachment"));
			if (!$replace) $replace = $this->searchDefinitions("background", $storage, array("color", "image", "position"));
			if (!$replace) $replace = $this->searchDefinitions("background", $storage, array("color", "image"));
			// Without Color
			if (!$replace) $replace = $this->searchDefinitions("background", $storage, array("image", "attachment", "position"));
			if (!$replace) $replace = $this->searchDefinitions("background", $storage, array("image", "repeat", "position"));
			if (!$replace) $replace = $this->searchDefinitions("background", $storage, array("image", "repeat", "attachment"));
			if (!$replace) $replace = $this->searchDefinitions("background", $storage, array("image", "repeat"));
			if (!$replace) $replace = $this->searchDefinitions("background", $storage, array("image", "attachment"));
			if (!$replace) $replace = $this->searchDefinitions("background", $storage, array("image", "position"));
			if (!$replace) $replace = $this->searchDefinitions("background", $storage, array("image"));
			// Just Color
			if (!$replace) $replace = $this->searchDefinitions("background", $storage, array("color"));

			// If replacement string found, run it on all options
			if ($replace){
				for ($i=0; $i<count($matches[1]); $i++){
					// Escape out url braces
					$matches[0][$i] = str_replace("(", "\(", $matches[0][$i]);
					$matches[0][$i] = str_replace(")", "\)", $matches[0][$i]);
					$v = preg_replace("(".$matches[0][$i].")is", $replace, $v);
				}
			}

			// Replace details
			$this->details[$k] = $v;
		}
	}

	function combineListProperties(){
		foreach ($this->details as $k=>$v){
			$storage = array();
			$pattern = "/list-style-(type|position|image):(.*?);/i";
			preg_match_all($pattern, $this->details[$k], $matches);
			for ($i=0; $i<count($matches[1]); $i++){
				// Store secondhand prop
				$storage[$matches[1][$i]] = $matches[2][$i];
			}

			// Run search patterns for replacement string
			$replace = $this->searchDefinitions("list-style", $storage, array("type", "position", "image"));
			if (!$replace) $replace = $this->searchDefinitions("list-style", $storage, array("type", "position"));
			if (!$replace) $replace = $this->searchDefinitions("list-style", $storage, array("type", "image"));
			if (!$replace) $replace = $this->searchDefinitions("list-style", $storage, array("position", "image"));
			if (!$replace) $replace = $this->searchDefinitions("list-style", $storage, array("type"));
			if (!$replace) $replace = $this->searchDefinitions("list-style", $storage, array("position"));
			if (!$replace) $replace = $this->searchDefinitions("list-style", $storage, array("image"));

			// If replacement string found, run it on all options
			if ($replace){
				for ($i=0; $i<count($matches[1]); $i++){
					// Escape out url braces
					$matches[0][$i] = str_replace("(", "\(", $matches[0][$i]);
					$matches[0][$i] = str_replace(")", "\)", $matches[0][$i]);
					$v = preg_replace("(".$matches[0][$i].")is", $replace, $v);
				}
			}

			// Replace details
			$this->details[$k] = $v;
		}
	}

	function searchDefinitions($prop, $storage, $search){
		$str = "$prop:";
		foreach ($search as $value){
			if (!isset($storage[$value])) return false;
			$str .= $storage[$value]." ";
		}
		return trim($str).";";
	}

	function removeMultipleDefinitions(){
		foreach($this->details as $k=>$v){
			$storage = array();
			$arr = explode(";", $v);
			foreach($arr as $x){
				if ($x){
					list($a, $b) = explode(":", $x);
					$storage[$a] = $b;
				}
			}
			if ($storage){
				unset($this->details[$k]);
				foreach($storage as $x=>$y){
					$this->details[$k] .= "$x:$y;";
				}
			}
		}
	}
};

$CSSC = new CSSCompression;
?>
Return current item: CSS Compressor