Blame | Last modification | View Log | RSS feed
<?php
class CSSParser {
protected static $cssCounter = 0;
protected static $propCounter = 0;
protected $cssData;
function __construct() {
$this->cssData = array();
}
public function ParseCSS($css) {
$index = ++self::$cssCounter;
$this->cssData[$index] = array('all'=>array(),'screen'=>array(),'print'=>array());
return $this->ParseCode($index, $css);
}
public function ParseCSSAppend($index, $css) {
if(isset($this->cssData[$index])) {
return $this->ParseCode($index, $css);
} else {
return $this->ParseCSS($css);
}
}
public function ParseCSSMediaAppend($index, $css, $media) {
$css = '@'.$media.' {'.PHP_EOL.$css.PHP_EOL.'}'.PHP_EOL;
if(isset($this->cssData[$index])) {
return $this->ParseCode($index, $css);
}
$index = ++self::$cssCounter;
$this->cssData[$index] = array('all'=>array(),'screen'=>array(),'print'=>array());
return $this->ParseCode($index, $css);
}
protected function ParseCode($index, $css) {
$currentMedia = 'all';
$mediaList = array();
$section = false;
$css = trim($css);
if(strlen($css) == 0) {
return $index;
}
$css = preg_replace('/\/\*.*\*\//Us', '', $css);
while(preg_match('/^\s*(\@(media|import|local)([^\{\}]+)(\{)|([^\{\}]+)(\{)|([^\{\}]*)(\}))/Usi', $css, $match)) {
if(isset($match[8]) && ($match[8] == '}')) {
if($section !== false) {
$code = trim($match[7]);
$idx = 0;
$inQuote = false;
$property = false;
$codeLen = strlen($code);
$parenthesis = array();
while($idx < $codeLen && isset($code{$idx})) {
$c = $code{$idx};
$idx++;
if($inQuote !== false) {
if($inQuote === $c) {
$inQuote = false;
}
} elseif(($inQuote === false) && ($c == '(')) {
array_push($parenthesis, '(');
} elseif(($inQuote === false) && ($c == ')')) {
array_pop($parenthesis);
} elseif(($c == '\'') || ($c == '"')) {
$inQuote = $c;
} elseif(($property === false) && ($c == ':')) {
$property = trim(substr($code, 0, $idx - 1));
if(preg_match('/^(.*)\[([0-9]*)\]$/Us', $property, $propMatch)) {
$property = $propMatch[1].'['.static::$propCounter.']';
static::$propCounter += 1;
}
$code = substr($code, $idx);
$idx = 0;
} elseif((count($parenthesis) == 0) && ($c == ';')) {
$value = trim(substr($code, 0, $idx - 1));
$code = substr($code, $idx);
$idx = 0;
$this->AddProperty($index, $currentMedia, $section, $property, $value);
$property = false;
}
}
if(($idx > 0) && ($property !== false)) {
$value = trim($code);
$this->AddProperty($index, $currentMedia, $section, $property, $value);
}
$section = false;
} elseif(count($mediaList) > 0) {
array_pop($mediaList);
if(count($mediaList) > 0) {
$currentMedia = end($mediaList);
} else {
$currentMedia = 'all';
}
} else {
// Superfluous }
}
} elseif(isset($match[6]) && ($match[6] == '{')) {
// Section
$section = trim($match[5]);
if(!isset($this->cssData[$index][$currentMedia][$section])) {
$this->cssData[$index][$currentMedia][$section] = array();
}
} elseif(isset($match[4]) && ($match[4] == '{')) {
if($match[2] == 'media') {
// New media
$media = trim($match[3]);
$mediaList[] = $media;
$currentMedia = $media;
if(!isset($this->cssData[$index][$currentMedia])) {
$this->cssData[$index][$currentMedia] = array();
}
} elseif($match[2] == 'import') {
// Can't support import (yet!)
} elseif($match[2] == 'local') {
// Can't support local import (yet!)
}
} else {
// Shouldn't get here
}
$stripCount = strlen($match[0]);
$css = trim(substr($css, $stripCount));
}
$css = trim($css);
if(strlen($css) > 0) {
echo "Potential error in stylesheet\n".$css;
}
return $index;
}
public function AddProperty($index, $media, $section, $property, $value) {
if(!isset($this->cssData[$index])) {
$this->cssData[$index] = array('all'=>array(),'screen'=>array(),'print'=>array());
}
$media = trim($media);
if($media == '') {
$media = 'all';
}
$section = trim($section);
$property = trim($property);
if(strlen($property) > 0) {
$value = trim($value);
if($media == 'all') {
$this->cssData[$index][$media][$section][$property] = $value;
$keys = array_keys($this->cssData[$index]);
foreach($keys as $key) {
if(!isset($this->cssData[$index][$key][$section])) {
$this->cssData[$index][$key][$section] = array();
}
$this->cssData[$index][$key][$section][$property] = $value;
}
} else {
if(!isset($this->cssData[$index][$media])) {
$this->cssData[$index][$media] = $this->cssData[$index]['all'];
}
if(!isset($this->cssData[$index][$media][$section])) {
$this->cssData[$index][$media][$section] = array();
}
$this->cssData[$index][$media][$section][$property] = $value;
}
}
}
public function GetMediaList($index) {
if(isset($this->cssData[$index])) {
return array_keys($this->cssData[$index]);
}
return array();
}
public function ExportKeyValues($index, $media, $keys) {
$result = array();
if(is_string($keys)) {
$keys = array($keys);
}
if(!is_array($keys)) {
return $result;
}
if(isset($this->cssData[$index]) && isset($this->cssData[$index][$media])) {
foreach($this->cssData[$index][$media] as $section => $sectionValues) {
foreach($sectionValues as $property => $value) {
if(in_array($property, $keys)) {
$result[] = $value;
}
}
}
}
return $result;
}
public function ExportMedia($index, $media, $block = false) {
$result = '';
if(isset($this->cssData[$index]) && isset($this->cssData[$index][$media])) {
foreach($this->cssData[$index][$media] as $section => $sectionValues) {
$result .= "$section {\n";
foreach($sectionValues as $property => $value) {
$property = preg_replace('/(\[[0-9]*\])$/Usi', '', $property);
if(is_array($block) && isset($block[$property])) {
if(preg_match('/^'.static::ConvertWildcards($block[$property]).'$/Usi', $value) == 0) {
$result .= $indent." $property: $value;\n";
}
} else {
$result .= " $property: $value;\n";
}
}
$result .= "}\n\n";
}
}
return $result;
}
public function ExportStyle($index, $block = false) {
$result = '';
if(isset($this->cssData[$index])) {
foreach($this->cssData[$index] as $media => $mediaValues) {
if($media != 'all') {
$result .= "@media $media {\n";
$indent = ' ';
} else {
$indent = '';
}
foreach($mediaValues as $section => $sectionValues) {
$result .= $indent."$section {\n";
foreach($sectionValues as $property => $value) {
$property = preg_replace('/(\[[0-9]*\])$/Usi', '', $property);
if(is_array($block) && isset($block[$property])) {
if(preg_match('/^'.static::ConvertWildcards($block[$property]).'$/Usi', $value) == 0) {
$result .= $indent." $property: $value;\n";
}
} else {
$result .= $indent." $property: $value;\n";
}
}
$result .= $indent."}\n\n";
}
if($media != 'all') {
$result .= "}\n\n";
}
}
}
return $result;
}
public function GetSections($index, $media = 'screen') {
if(isset($this->cssData[$index])) {
if(isset($this->cssData[$index][$media])) {
return array_keys($this->cssData[$index][$media]);
}
if(isset($this->cssData[$index]['all'])) {
return array_keys($this->cssData[$index]['all']);
}
}
return false;
}
public function GetSectionsFiltered($index, $matchKey, $matchValue, $media = 'screen') {
if(isset($this->cssData[$index])) {
if(!isset($this->cssData[$index][$media])) {
$media = 'all';
if(!isset($this->cssData[$index][$media])) {
return false;
}
}
if(is_array($this->cssData[$index][$media])) {
$result = array();
foreach($this->cssData[$index][$media] as $section => $values) {
if(isset($values[$matchKey])) {
if($values[$matchKey] == $matchValue) {
$result[] = $section;
}
}
}
return $result;
}
}
return false;
}
public function GetEditorCSS($index) {
$forbiddenKeys = array();
$forbiddenKeys[] = '^filter$';
return $this->GetCSS($index, 'screen', $forbiddenKeys);
}
public function GetCSS($index, $media = 'screen', $forbiddenKeys = array()) {
if(isset($this->cssData[$index])) {
if(!isset($this->cssData[$index][$media])) {
$media = 'all';
if(!isset($this->cssData[$index][$media])) {
return false;
}
}
if(is_array($this->cssData[$index][$media])) {
$result = '';
foreach($this->cssData[$index][$media] as $section => $values) {
$result .= $section.' {';
$result .= "\n";
if(is_array($values)) {
foreach($values as $key => $value) {
$skipThis = false;
foreach($forbiddenKeys as $fKey) {
if(preg_match('/'.$fKey.'/Usi', $key)) {
$skipThis = true;
break;
}
}
if($skipThis) {
continue;
}
$result .= ' ';
$key = preg_replace('/(\[[0-9]*\])$/Usi', '', $key);
$result .= $key.': '.$value.';';
$result .= "\n";
}
}
$result .= '}';
$result .= "\n";
}
return $result;
}
}
return false;
}
public function GetCSSFiltered($index, $matchKey, $matchValue, $media = 'screen') {
if(!isset($this->cssData[$index])) {
if(!isset($this->cssData[$index][$media])) {
$media = 'all';
if(!isset($this->cssData[$index][$media])) {
return false;
}
}
}
$sections = $this->GetSectionsFiltered($index, $matchKey, $matchValue, $media);
if($sections !== false) {
if(is_array($sections)) {
$result = '';
foreach($sections as $section) {
$result .= $section.' {';
$temp = $this->cssData[$index][$media];
if(is_array($temp)) {
foreach($temp as $key => $value) {
$key = preg_replace('/(\[[0-9]*\])$/Usi', '', $key);
$result .= $key.': '.$value.';';
}
}
$result .= $section.' }';
}
return $result;
}
}
return false;
}
public function GetCSSArray($index, $media = 'screen') {
if(isset($this->cssData[$index])) {
if(isset($this->cssData[$index][$media])) {
return $this->cssData[$index][$media];
} elseif(isset($this->cssData[$index]['all'])) {
return $this->cssData[$index][$media];
}
}
return false;
}
public static function ConvertWildcards($text) {
$text = str_replace('.', '\.', $text);
$text = str_replace('*', '.*', $text);
$text = str_replace('?', '.', $text);
return $text;
}
}