1<?php
2
3/*
4 * This file is part of the Assetic package, an OpenSky project.
5 *
6 * (c) 2010-2014 OpenSky Project Inc
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Assetic\Filter;
13
14use Assetic\Asset\AssetInterface;
15use Assetic\Factory\AssetFactory;
16use Assetic\Util\LessUtils;
17
18/**
19 * Loads LESS files using the PHP implementation of less, lessphp.
20 *
21 * Less files are mostly compatible, but there are slight differences.
22 *
23 * @link http://leafo.net/lessphp/
24 *
25 * @author David Buchmann <david@liip.ch>
26 * @author Kris Wallsmith <kris.wallsmith@gmail.com>
27 */
28class LessphpFilter implements DependencyExtractorInterface
29{
30    private $presets = array();
31    private $formatter;
32    private $preserveComments;
33    private $customFunctions = array();
34    private $options = array();
35
36    /**
37     * Lessphp Load Paths
38     *
39     * @var array
40     */
41    protected $loadPaths = array();
42
43    /**
44     * Adds a load path to the paths used by lessphp
45     *
46     * @param string $path Load Path
47     */
48    public function addLoadPath($path)
49    {
50        $this->loadPaths[] = $path;
51    }
52
53    /**
54     * Sets load paths used by lessphp
55     *
56     * @param array $loadPaths Load paths
57     */
58    public function setLoadPaths(array $loadPaths)
59    {
60        $this->loadPaths = $loadPaths;
61    }
62
63    public function setPresets(array $presets)
64    {
65        $this->presets = $presets;
66    }
67
68    public function setOptions(array $options)
69    {
70    	$this->options = $options;
71    }
72
73    /**
74     * @param string $formatter One of "lessjs", "compressed", or "classic".
75     */
76    public function setFormatter($formatter)
77    {
78        $this->formatter = $formatter;
79    }
80
81    /**
82     * @param boolean $preserveComments
83     */
84    public function setPreserveComments($preserveComments)
85    {
86        $this->preserveComments = $preserveComments;
87    }
88
89    public function filterLoad(AssetInterface $asset)
90    {
91        $lc = new \lessc();
92        if ($dir = $asset->getSourceDirectory()) {
93            $lc->importDir = $dir;
94        }
95
96        foreach ($this->loadPaths as $loadPath) {
97            $lc->addImportDir($loadPath);
98        }
99
100        foreach ($this->customFunctions as $name => $callable) {
101            $lc->registerFunction($name, $callable);
102        }
103
104        if ($this->formatter) {
105            $lc->setFormatter($this->formatter);
106        }
107
108        if (null !== $this->preserveComments) {
109            $lc->setPreserveComments($this->preserveComments);
110        }
111
112        if (method_exists($lc, 'setOptions') && count($this->options) > 0 ) {
113        	$lc->setOptions($this->options);
114        }
115
116        $asset->setContent($lc->parse($asset->getContent(), $this->presets));
117    }
118
119    public function registerFunction($name, $callable)
120    {
121        $this->customFunctions[$name] = $callable;
122    }
123
124    public function filterDump(AssetInterface $asset)
125    {
126    }
127
128    public function getChildren(AssetFactory $factory, $content, $loadPath = null)
129    {
130        $loadPaths = $this->loadPaths;
131        if (null !== $loadPath) {
132            $loadPaths[] = $loadPath;
133        }
134
135        if (empty($loadPaths)) {
136            return array();
137        }
138
139        $children = array();
140        foreach (LessUtils::extractImports($content) as $reference) {
141            if ('.css' === substr($reference, -4)) {
142                // skip normal css imports
143                // todo: skip imports with media queries
144                continue;
145            }
146
147            if ('.less' !== substr($reference, -5)) {
148                $reference .= '.less';
149            }
150
151            foreach ($loadPaths as $loadPath) {
152                if (file_exists($file = $loadPath.'/'.$reference)) {
153                    $coll = $factory->createAsset($file, array(), array('root' => $loadPath));
154                    foreach ($coll as $leaf) {
155                        $leaf->ensureFilter($this);
156                        $children[] = $leaf;
157                        goto next_reference;
158                    }
159                }
160            }
161
162            next_reference:
163        }
164
165        return $children;
166    }
167}
168