* @copyright Copyright (c) 2010 PBM Web Development * @license http://phamlp.googlecode.com/files/license.txt * @package PHamlP * @subpackage Sass.script */ /** * SassScriptFunction class. * Preforms a SassScript function. * @package PHamlP * @subpackage Sass.script */ class SassScriptFunction { /**@#+ * Regexes for matching and extracting functions and arguments */ const MATCH = '/^(((-\w)|(\w))[-\w]*)\(/'; const MATCH_FUNC = '/^((?:(?:-\w)|(?:\w))[-\w]*)\((.*)\)/'; const SPLIT_ARGS = '/\s*((?:[\'"].*?["\'])|(?:.+?(?:\(.*\).*?)?))\s*(?:,|$)/'; const NAME = 1; const ARGS = 2; private $name; private $args; /** * SassScriptFunction constructor * @param string name of the function * @param array arguments for the function * @return SassScriptFunction */ public function __construct($name, $args) { $this->name = $name; $this->args = $args; } /** * Evaluates the function. * Look for a user defined function first - this allows users to override * pre-defined functions, then try the pre-defined functions. * @return Function the value of this Function * @throws SassScriptFunctionException if function is undefined */ public function perform() { $name = str_replace('-', '_', $this->name); foreach (SassScriptParser::$context->node->parser->function_paths as $path) { $_path = explode(DIRECTORY_SEPARATOR, $path); $_class = ucfirst($_path[sizeof($_path) - 2]); foreach (array_slice(scandir($path), 2) as $file) { $filename = $path . DIRECTORY_SEPARATOR . $file; if (is_file($filename)) { require_once($filename); $class = 'SassExtentions'.$_class.'Functions'. ucfirst(substr($file, 0, -4)); if (method_exists($class, $name)) { return call_user_func_array(array($class, $name), $this->args); } } } // foreach } // foreach require_once('SassScriptFunctions.php'); if (method_exists('SassScriptFunctions', $name)) { return call_user_func_array(array('SassScriptFunctions', $name), $this->args); } // CSS function: create a SassString that will emit the function into the CSS $args = array(); foreach ($this->args as $arg) { $args[] = $arg->toString(); } return new SassString($this->name . '(' . join(', ', $args) . ')'); } /** * Imports files in the specified directory. * @param string path to directory to import * @return array filenames imported */ private function import($dir) { $files = array(); foreach (array_slice(scandir($dir), 2) as $file) { if (is_file($dir . DIRECTORY_SEPARATOR . $file)) { $files[] = $file; require_once($dir . DIRECTORY_SEPARATOR . $file); } } // foreach return $files; } /** * Returns a value indicating if a token of this type can be matched at * the start of the subject string. * @param string the subject string * @return mixed match at the start of the string or false if no match */ public static function isa($subject) { if (!preg_match(self::MATCH, $subject, $matches)) return false; $match = $matches[0]; $paren = 1; $strpos = strlen($match); $strlen = strlen($subject); while($paren && $strpos < $strlen) { $c = $subject[$strpos++]; $match .= $c; if ($c === '(') { $paren += 1; } elseif ($c === ')') { $paren -= 1; } } return $match; } public static function extractArgs($string) { $args = array(); $arg = ''; $paren = 0; $strpos = 0; $strlen = strlen($string); while ($strpos < $strlen) { $c = $string[$strpos++]; switch ($c) { case '(': $paren += 1; $arg .= $c; break; case ')': $paren -= 1; $arg .= $c; break; case '"': case "'": $arg .= $c; do { $_c = $string[$strpos++]; $arg .= $_c; } while ($_c !== $c); break; case ',': if ($paren) { $arg .= $c; break; } $args[] = trim($arg); $arg = ''; break; default: $arg .= $c; break; } } if ($arg) $args[] = trim($arg); return $args; } }