<?
/**
* Object Oriented CSS v1.0
* August 2, 2009
* Corey Hart @ http://www.codenothing.com
*/
// Cache Directory & Debugging Mode
define('OOCSS_CACHE_DIR', dirname(__FILE__).'/cache/');
define('OOCSS_DEBUG_MODE', false);
Class ObjectOrientedCSS
{
var $vars = array();
var $file = '';
var $file_path = '';
var $cache_path = '';
function __construct(){
// File Path
$this->getFilePath();
// Cached Files are stored based on file change times of
// this file and the file requested
$time['file'] = date('Y-m-d H:i:s', filemtime($this->file_path));
$time['oocss'] = date('Y-m-d H:i:s', filemtime(__FILE__));
$this->cache_path = OOCSS_CACHE_DIR.md5($this->file_path . $time['file'] . $time['oocss']).'.css';
// If there is no cached file, parse requested file
if (!is_file($this->cache_path))
$this->runParse();
}
function getFilePath(){
// Use current selected path if possible
$this->file_path = $_SERVER['DOCUMENT_ROOT'] . $_SERVER['REDIRECT_URL'];
if (! is_file($this->file_path)){
// See if there's a css extension of the file
$this->file_path = preg_replace("/\.oocss$/i", '.css', $this->file_path);
if (is_file($this->file_path)){
return $this->file_path;
} else {
die('Could not find a stylesheet');
}
}
}
function runParse(){
// Raw file trimmed down to single line
$this->getFileContent($this->file_path);
// Run variable conversions first
$this->variableReplacement();
// Loop through and create the tree
$tree = $this->convertLoop($this->prepFileForLoop());
// Process tree for output
$this->file = $this->processFile($tree);
// Cache converted file
$this->outputFile($tree);
}
function getFileContent($file){
// Trim file down to single line
$this->file = trim(file_get_contents($file));
$this->file = preg_replace("/\n|\r|\t/", ' ', $this->file);
$this->file = preg_replace("/\/\*(.*?)\*\//", '', $this->file);
$this->file = preg_replace("/\s{2,}/", ' ', $this->file);
}
function variableReplacement(){
preg_match_all("/([$][a-zA-Z0-9_]+)\s*=\s*([^;]+);/", $this->file, $matches);
for ($i=0, $imax=count($matches[0]); $i<$imax; $i++){
// Matches
$string = $matches[0][$i];
$var = $matches[1][$i];
$value = $matches[2][$i];
// Check for multiple definitions
if (strpos($value, '{') !== false){
$value = str_replace('{', '', $value);
$value = str_replace('}', '', $value);
$value = str_replace(',', ';', $value);
}
// Store the var for debugging
$this->vars[$var] = $value;
// Remove the declaration from the file
$this->file = str_replace($string, '', $this->file);
// Enforce semi-colon replacement
$this->file = str_replace($var.';', $value.';', $this->file);
}
}
function prepFileForLoop(){
// Remove all comments
$this->file = preg_replace("/\/\*(.*?)\*\//", '', $this->file);
// Line out as much as possible
$this->file = preg_replace("/([{]|[}]|;)/", "$1\n", $this->file);
// Return each line
return explode("\n", str_replace('}', "\n}", $this->file));
}
function convertLoop(&$file){
$ret = $old = array();
while ($file){
$line = trim(array_shift($file));
if (!$line || $line == ''){
continue;
}
else if (strpos($line, '{') !== false){
// Take out the tag
$tag = trim(array_shift(explode('{', $line)));
// Store old tag for merging
if (isset($ret[$tag]))
$old = $ret;
// Run recursive loop of all levels
$ret[$tag] = $this->convertLoop($file);
// If old tag found, merge results
if ($old){
$ret = array_merge_recursive($old, $ret);
$old = array();
}
}
else if (strpos($line, '}') !== false){
return $ret;
}
else{
$ret['props'][] = $line;
}
}
return $ret;
}
function processFile($file, $tag=''){
$levels = array();
foreach ($file as $key => $value){
if ($key == 'props'){
foreach ($value as $prop)
$str .= "\t$prop\n";
}
else if (is_array($value)){
$levels[$key] = $value;
}
}
// Only add string if it exists with a tag
if (($tag = trim($tag)) && $tag != '' && $str != ''){
// Remove Lingering direct descendent tag and trim it
$trimedTag = trim(preg_replace('/[>]$/', '', $tag));
$str = "$trimedTag {\n$str}\n\n";
}
// Loop through nested levels
foreach ($levels as $key=>$value)
$str .= $this->processFile($value, $tag.' '.$key);
// Return concatenated string
return $str;
}
function outputFile($tree){
// Add commented out notes in debug mode
if (OOCSS_DEBUG_MODE){
$str = "/*******\nOOCSS DEBUGGING TREES\n\n";
$str .= "Variables Stored: ";
$str .= print_r($this->vars, true);
$str .= "\n\nTree Parsed: ";
$str .= print_r($tree, true);
$str .= "******/\n\n\n\n";
// Prepend string to file
$this->file = $str.$this->file;
}
// Cache compression
$fh = fopen($this->cache_path, 'w');
fwrite($fh, $this->file);
fclose($fh);
}
function __destruct(){
header('Content-type: text/css');
readfile($this->cache_path);
}
};
new ObjectOrientedCSS;
?>