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