fix image text
[pear] / HTML / Less.php
1 <?php
2
3 /**
4  * This file provides the part of lessphp API (https://github.com/leafo/lessphp)
5  * to be a drop-in replacement for following products:
6  *  - Drupal 7, by the less module v3.0+ (https://drupal.org/project/less)
7  *  - Symfony 2
8  *  https://github.com/oyejorge/less.php
9  */
10 require_once 'HTML/Less/Version.php';
11
12 class HTML_Less {
13
14     static public $VERSION = HTML_Less_Version::less_version;
15     public $importDir = '';
16     protected $allParsedFiles = array();
17     protected $libFunctions = array();
18     var $registeredVars = array();
19     private $formatterName;
20     private $options = array();
21
22     public function __construct($lessc = null, $sourceName = null) 
23     {
24         
25     }
26
27     public function setImportDir($dirs) 
28     {
29         $this->importDir = (array) $dirs;
30     }
31
32     public function addImportDir($dir) 
33     {
34         $this->importDir = (array) $this->importDir;
35         $this->importDir[] = $dir;
36     }
37
38     public function setFormatter($name) 
39     {
40         $this->formatterName = $name;
41     }
42
43     public function setPreserveComments($preserve) 
44     {
45         
46     }
47
48     public function registerFunction($name, $func) 
49     {
50         $this->libFunctions[$name] = $func;
51     }
52
53     public function unregisterFunction($name) 
54     {
55         unset($this->libFunctions[$name]);
56     }
57
58     public function setVariables($variables) 
59     {
60         foreach ($variables as $name => $value) {
61             $this->setVariable($name, $value);
62         }
63     }
64
65     public function setVariable($name, $value) 
66     {
67         $this->registeredVars[$name] = $value;
68     }
69
70     public function unsetVariable($name) 
71     {
72         unset($this->registeredVars[$name]);
73     }
74
75     public function setOptions($options) 
76     {
77         foreach ($options as $name => $value) {
78             $this->setOption($name, $value);
79         }
80     }
81
82     public function setOption($name, $value) 
83     {
84         $this->options[$name] = $value;
85     }
86
87     public function parse($buffer, $presets = array()) 
88     {
89         $this->setVariables($presets);
90
91         require_once 'HTML/Less/Parser.php';
92         
93         $parser = new HTML_Less_Parser($this->getOptions());
94         $parser->setImportDirs($this->getImportDirs());
95         foreach ($this->libFunctions as $name => $func) {
96             $parser->registerFunction($name, $func);
97         }
98         $parser->parse($buffer);
99         if (count($this->registeredVars)) {
100             $parser->ModifyVars($this->registeredVars);
101         }
102         return $parser->getCss();
103     }
104
105     protected function getOptions() 
106     {
107         $options = array('relativeUrls' => false);
108         switch ($this->formatterName) {
109             case 'compressed':
110                 $options['compress'] = true;
111                 break;
112         }
113         if (is_array($this->options)) {
114             $options = array_merge($options, $this->options);
115         }
116         return $options;
117     }
118
119     protected function getImportDirs() 
120     {
121         $dirs_ = (array) $this->importDir;
122         $dirs = array();
123         foreach ($dirs_ as $dir) {
124             $dirs[$dir] = '';
125         }
126         return $dirs;
127     }
128
129     public function compile($string, $name = null) 
130     {
131         $oldImport = $this->importDir;
132         $this->importDir = (array) $this->importDir;
133
134         $this->allParsedFiles = array();
135
136         require_once 'HTML/Less/Parser.php';
137         
138         $parser = new HTML_Less_Parser($this->getOptions());
139         $parser->SetImportDirs($this->getImportDirs());
140         if (count($this->registeredVars)) {
141             $parser->ModifyVars($this->registeredVars);
142         }
143         foreach ($this->libFunctions as $name => $func) {
144             $parser->registerFunction($name, $func);
145         }
146         $parser->parse($string);
147         $out = $parser->getCss();
148
149         $parsed = HTML_Less_Parser::AllParsedFiles();
150         foreach ($parsed as $file) {
151             $this->addParsedFile($file);
152         }
153
154         $this->importDir = $oldImport;
155
156         return $out;
157     }
158
159     public function compileFile($fname, $outFname = null) 
160     {
161         if (!is_readable($fname)) {
162             throw new Exception('load error: failed to find ' . $fname);
163         }
164
165         require_once 'HTML/Less/Parser.php';
166         
167         $pi = pathinfo($fname);
168
169         $oldImport = $this->importDir;
170
171         $this->importDir = (array) $this->importDir;
172         $this->importDir[] = HTML_Less_Parser::AbsPath($pi['dirname']) . '/';
173
174         $this->allParsedFiles = array();
175         $this->addParsedFile($fname);
176
177         $parser = new HTML_Less_Parser($this->getOptions());
178         $parser->SetImportDirs($this->getImportDirs());
179         if (count($this->registeredVars)) {
180             $parser->ModifyVars($this->registeredVars);
181         }
182         foreach ($this->libFunctions as $name => $func) {
183             $parser->registerFunction($name, $func);
184         }
185         $parser->parseFile($fname);
186         
187         //print_R($parser->getVariables());
188         
189         $out = $parser->getCss();
190
191         $parsed = HTML_Less_Parser::AllParsedFiles();
192         foreach ($parsed as $file) {
193             $this->addParsedFile($file);
194         }
195
196         $this->importDir = $oldImport;
197
198         if ($outFname !== null) {
199             return file_put_contents($outFname, $out);
200         }
201
202         return $out;
203     }
204
205     public function checkedCompile($in, $out) 
206     {
207         if (!is_file($out) || filemtime($in) > filemtime($out)) {
208             $this->compileFile($in, $out);
209             return true;
210         }
211         return false;
212     }
213
214     /**
215      * Execute lessphp on a .less file or a lessphp cache structure
216      *
217      * The lessphp cache structure contains information about a specific
218      * less file having been parsed. It can be used as a hint for future
219      * calls to determine whether or not a rebuild is required.
220      *
221      * The cache structure contains two important keys that may be used
222      * externally:
223      *
224      * compiled: The final compiled CSS
225      * updated: The time (in seconds) the CSS was last compiled
226      *
227      * The cache structure is a plain-ol' PHP associative array and can
228      * be serialized and unserialized without a hitch.
229      *
230      * @param mixed $in Input
231      * @param bool $force Force rebuild?
232      * @return array lessphp cache structure
233      */
234     public function cachedCompile($in, $force = false) 
235     {
236         // assume no root
237         $root = null;
238
239         if (is_string($in)) {
240             $root = $in;
241         } elseif (is_array($in) and isset($in['root'])) {
242             if ($force or ! isset($in['files'])) {
243                 // If we are forcing a recompile or if for some reason the
244                 // structure does not contain any file information we should
245                 // specify the root to trigger a rebuild.
246                 $root = $in['root'];
247             } elseif (isset($in['files']) and is_array($in['files'])) {
248                 foreach ($in['files'] as $fname => $ftime) {
249                     if (!file_exists($fname) or filemtime($fname) > $ftime) {
250                         // One of the files we knew about previously has changed
251                         // so we should look at our incoming root again.
252                         $root = $in['root'];
253                         break;
254                     }
255                 }
256             }
257         } else {
258             // TODO: Throw an exception? We got neither a string nor something
259             // that looks like a compatible lessphp cache structure.
260             return null;
261         }
262
263         if ($root !== null) {
264             // If we have a root value which means we should rebuild.
265             $out = array();
266             $out['root'] = $root;
267             $out['compiled'] = $this->compileFile($root);
268             $out['files'] = $this->allParsedFiles();
269             $out['updated'] = time();
270             return $out;
271         } else {
272             // No changes, pass back the structure
273             // we were given initially.
274             return $in;
275         }
276     }
277
278     public function ccompile($in, $out, $less = null) 
279     {
280         if ($less === null) {
281             $less = new self;
282         }
283         return $less->checkedCompile($in, $out);
284     }
285
286     public static function cexecute($in, $force = false, $less = null) 
287     {
288         if ($less === null) {
289             $less = new self;
290         }
291         return $less->cachedCompile($in, $force);
292     }
293
294     public function allParsedFiles() 
295     {
296         return $this->allParsedFiles;
297     }
298
299     protected function addParsedFile($file) 
300     {
301         require_once 'HTML/Less/Parser.php';
302         $this->allParsedFiles[HTML_Less_Parser::AbsPath($file)] = filemtime($file);
303     }
304
305 }