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