3 * This file provides the part of lessphp API (https://github.com/leafo/lessphp)
4 * to be a drop-in replacement for following products:
5 * - Drupal 7, by the less module v3.0+ (https://drupal.org/project/less)
9 // Register autoloader for non-composer installations
10 if ( !class_exists( 'Less_Parser' ) ) {
11 require_once __DIR__ . '/lib/Less/Autoloader.php';
12 Less_Autoloader::register();
17 static public $VERSION = Less_Version::less_version;
19 public $importDir = '';
20 protected $allParsedFiles = array();
21 protected $libFunctions = array();
22 protected $registeredVars = array();
23 private $formatterName;
24 private $options = array();
26 public function __construct( $lessc=null, $sourceName=null ) {}
28 public function setImportDir( $dirs ) {
29 $this->importDir = (array)$dirs;
32 public function addImportDir( $dir ) {
33 $this->importDir = (array)$this->importDir;
34 $this->importDir[] = $dir;
37 public function setFormatter( $name ) {
38 $this->formatterName = $name;
41 public function setPreserveComments( $preserve ) {}
43 public function registerFunction( $name, $func ) {
44 $this->libFunctions[$name] = $func;
47 public function unregisterFunction( $name ) {
48 unset( $this->libFunctions[$name] );
51 public function setVariables( $variables ){
52 foreach ( $variables as $name => $value ) {
53 $this->setVariable( $name, $value );
57 public function setVariable( $name, $value ) {
58 $this->registeredVars[$name] = $value;
61 public function unsetVariable( $name ) {
62 unset( $this->registeredVars[$name] );
65 public function setOptions( $options ) {
66 foreach ( $options as $name => $value ) {
67 $this->setOption( $name, $value);
71 public function setOption( $name, $value ) {
72 $this->options[$name] = $value;
75 public function parse( $buffer, $presets = array() ) {
77 $this->setVariables( $presets );
79 $parser = new Less_Parser( $this->getOptions() );
80 $parser->setImportDirs( $this->getImportDirs() );
81 foreach ( $this->libFunctions as $name => $func ) {
82 $parser->registerFunction( $name, $func );
84 $parser->parse($buffer);
85 if ( count( $this->registeredVars ) ) {
86 $parser->ModifyVars( $this->registeredVars );
89 return $parser->getCss();
92 protected function getOptions() {
93 $options = array( 'relativeUrls'=>false );
94 switch( $this->formatterName ) {
96 $options['compress'] = true;
99 if (is_array($this->options))
101 $options = array_merge($options, $this->options);
106 protected function getImportDirs() {
107 $dirs_ = (array)$this->importDir;
109 foreach ( $dirs_ as $dir ) {
115 public function compile( $string, $name = null ) {
117 $oldImport = $this->importDir;
118 $this->importDir = (array)$this->importDir;
120 $this->allParsedFiles = array();
122 $parser = new Less_Parser( $this->getOptions() );
123 $parser->SetImportDirs( $this->getImportDirs() );
124 if ( count( $this->registeredVars ) ) {
125 $parser->ModifyVars( $this->registeredVars );
127 foreach ( $this->libFunctions as $name => $func ) {
128 $parser->registerFunction( $name, $func );
130 $parser->parse( $string );
131 $out = $parser->getCss();
133 $parsed = Less_Parser::AllParsedFiles();
134 foreach ( $parsed as $file ) {
135 $this->addParsedFile( $file );
138 $this->importDir = $oldImport;
143 public function compileFile( $fname, $outFname = null ) {
144 if ( !is_readable( $fname ) ) {
145 throw new Exception( 'load error: failed to find '.$fname );
148 $pi = pathinfo( $fname );
150 $oldImport = $this->importDir;
152 $this->importDir = (array)$this->importDir;
153 $this->importDir[] = Less_Parser::AbsPath( $pi['dirname'] ).'/';
155 $this->allParsedFiles = array();
156 $this->addParsedFile( $fname );
158 $parser = new Less_Parser( $this->getOptions() );
159 $parser->SetImportDirs( $this->getImportDirs() );
160 if ( count( $this->registeredVars ) ) {
161 $parser->ModifyVars( $this->registeredVars );
163 foreach ( $this->libFunctions as $name => $func ) {
164 $parser->registerFunction( $name, $func );
166 $parser->parseFile( $fname );
167 $out = $parser->getCss();
169 $parsed = Less_Parser::AllParsedFiles();
170 foreach ( $parsed as $file ) {
171 $this->addParsedFile( $file );
174 $this->importDir = $oldImport;
176 if ( $outFname !== null ) {
177 return file_put_contents( $outFname, $out );
183 public function checkedCompile( $in, $out ) {
184 if ( !is_file( $out ) || filemtime( $in ) > filemtime( $out ) ) {
185 $this->compileFile($in, $out);
193 * Execute lessphp on a .less file or a lessphp cache structure
195 * The lessphp cache structure contains information about a specific
196 * less file having been parsed. It can be used as a hint for future
197 * calls to determine whether or not a rebuild is required.
199 * The cache structure contains two important keys that may be used
202 * compiled: The final compiled CSS
203 * updated: The time (in seconds) the CSS was last compiled
205 * The cache structure is a plain-ol' PHP associative array and can
206 * be serialized and unserialized without a hitch.
208 * @param mixed $in Input
209 * @param bool $force Force rebuild?
210 * @return array lessphp cache structure
212 public function cachedCompile( $in, $force = false ) {
216 if ( is_string( $in ) ) {
218 } elseif ( is_array( $in ) and isset( $in['root'] ) ) {
219 if ( $force or ! isset( $in['files'] ) ) {
220 // If we are forcing a recompile or if for some reason the
221 // structure does not contain any file information we should
222 // specify the root to trigger a rebuild.
224 } elseif ( isset( $in['files'] ) and is_array( $in['files'] ) ) {
225 foreach ( $in['files'] as $fname => $ftime ) {
226 if ( !file_exists( $fname ) or filemtime( $fname ) > $ftime ) {
227 // One of the files we knew about previously has changed
228 // so we should look at our incoming root again.
235 // TODO: Throw an exception? We got neither a string nor something
236 // that looks like a compatible lessphp cache structure.
240 if ( $root !== null ) {
241 // If we have a root value which means we should rebuild.
243 $out['root'] = $root;
244 $out['compiled'] = $this->compileFile($root);
245 $out['files'] = $this->allParsedFiles();
246 $out['updated'] = time();
249 // No changes, pass back the structure
250 // we were given initially.
255 public function ccompile( $in, $out, $less = null ) {
256 if ( $less === null ) {
259 return $less->checkedCompile( $in, $out );
262 public static function cexecute( $in, $force = false, $less = null ) {
263 if ( $less === null ) {
266 return $less->cachedCompile($in, $force);
269 public function allParsedFiles() {
270 return $this->allParsedFiles;
273 protected function addParsedFile( $file ) {
274 $this->allParsedFiles[Less_Parser::AbsPath( $file )] = filemtime( $file );