CompassFilter.php 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. <?php
  2. /*
  3. * This file is part of the Assetic package, an OpenSky project.
  4. *
  5. * (c) 2010-2011 OpenSky Project Inc
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Assetic\Filter;
  11. use Assetic\Asset\AssetInterface;
  12. use Assetic\Filter\FilterInterface;
  13. use Assetic\Util\ProcessBuilder;
  14. /**
  15. * Loads Compass files.
  16. *
  17. * @author Maxime Thirouin <maxime.thirouin@gmail.com>
  18. */
  19. class CompassFilter implements FilterInterface
  20. {
  21. private $compassPath;
  22. private $scss;
  23. // sass options
  24. private $unixNewlines;
  25. private $debugInfo;
  26. private $cacheLocation;
  27. private $noCache;
  28. // compass options
  29. private $force;
  30. private $style;
  31. private $quiet;
  32. private $noLineComments;
  33. private $imagesDir;
  34. private $javascriptsDir;
  35. // compass configuration file options
  36. private $plugins = array();
  37. private $loadPaths = array();
  38. private $httpPath;
  39. private $httpImagesPath;
  40. private $httpJavascriptsPath;
  41. public function __construct($compassPath = '/usr/bin/compass')
  42. {
  43. $this->compassPath = $compassPath;
  44. $this->cacheLocation = sys_get_temp_dir();
  45. }
  46. public function setScss($scss)
  47. {
  48. $this->scss = $scss;
  49. }
  50. // sass options setters
  51. public function setUnixNewlines($unixNewlines)
  52. {
  53. $this->unixNewlines = $unixNewlines;
  54. }
  55. public function setDebugInfo($debugInfo)
  56. {
  57. $this->debugInfo = $debugInfo;
  58. }
  59. public function setCacheLocation($cacheLocation)
  60. {
  61. $this->cacheLocation = $cacheLocation;
  62. }
  63. public function setNoCache($noCache)
  64. {
  65. $this->noCache = $noCache;
  66. }
  67. // compass options setters
  68. public function setForce($force)
  69. {
  70. $this->force = $force;
  71. }
  72. public function setStyle($style)
  73. {
  74. $this->style = $style;
  75. }
  76. public function setQuiet($quiet)
  77. {
  78. $this->quiet = $quiet;
  79. }
  80. public function setNoLineComments($noLineComments)
  81. {
  82. $this->noLineComments = $noLineComments;
  83. }
  84. public function setImagesDir($imagesDir)
  85. {
  86. $this->imagesDir = $imagesDir;
  87. }
  88. public function setJavascriptsDir($javascriptsDir)
  89. {
  90. $this->javascriptsDir = $javascriptsDir;
  91. }
  92. // compass configuration file options setters
  93. public function setPlugins(array $plugins)
  94. {
  95. $this->plugins = $plugins;
  96. }
  97. public function addPlugin($plugin)
  98. {
  99. $this->plugins[] = $plugin;
  100. }
  101. public function addLoadPath($loadPath)
  102. {
  103. $this->loadPaths[] = $loadPath;
  104. }
  105. public function setHttpPath($httpPath)
  106. {
  107. $this->httpPath = $httpPath;
  108. }
  109. public function setHttpImagesPath($httpImagesPath)
  110. {
  111. $this->httpImagesPath = $httpImagesPath;
  112. }
  113. public function setHttpJavascriptsPath($httpJavascriptsPath)
  114. {
  115. $this->httpJavascriptsPath = $httpJavascriptsPath;
  116. }
  117. public function filterLoad(AssetInterface $asset)
  118. {
  119. $root = $asset->getSourceRoot();
  120. $path = $asset->getSourcePath();
  121. if ($root && $path) {
  122. $this->loadPaths[] = dirname($root.'/'.$path);
  123. }
  124. // compass does not seems to handle symlink, so we use realpath()
  125. $tempDir = realpath(sys_get_temp_dir());
  126. $pb = new ProcessBuilder();
  127. $pb
  128. ->inheritEnvironmentVariables()
  129. ->add($this->compassPath)
  130. ->add('compile')
  131. ->add($tempDir)
  132. ;
  133. if ($this->force) {
  134. $pb->add('--force');
  135. }
  136. if ($this->style) {
  137. $pb->add('--output-style')->add($this->style);
  138. }
  139. if ($this->quiet) {
  140. $pb->add('--quiet');
  141. }
  142. if ($this->noLineComments) {
  143. $pb->add('--no-line-comments');
  144. }
  145. // these two options are not passed into the config file
  146. // because like this, compass adapts this to be xxx_dir or xxx_path
  147. // whether it's an absolute path or not
  148. if ($this->imagesDir) {
  149. $pb->add('--images-dir')->add($this->imagesDir);
  150. }
  151. if ($this->javascriptsDir) {
  152. $pb->add('--javascripts-dir')->add($this->javascriptsDir);
  153. }
  154. // options in config file
  155. $optionsConfig = array();
  156. if (!empty($this->loadPaths)) {
  157. $optionsConfig['additional_import_paths'] = $this->loadPaths;
  158. }
  159. if ($this->unixNewlines) {
  160. $optionsConfig['sass_options']['unix_newlines'] = true;
  161. }
  162. if ($this->debugInfo) {
  163. $optionsConfig['sass_options']['debug_info'] = true;
  164. }
  165. if ($this->cacheLocation) {
  166. $optionsConfig['sass_options']['cache_location'] = $this->cacheLocation;
  167. }
  168. if ($this->noCache) {
  169. $optionsConfig['sass_options']['no_cache'] = true;
  170. }
  171. if ($this->httpPath) {
  172. $optionsConfig['http_path'] = $this->httpPath;
  173. }
  174. if ($this->httpImagesPath) {
  175. $optionsConfig['http_images_path'] = $this->httpImagesPath;
  176. }
  177. if ($this->httpJavascriptsPath) {
  178. $optionsConfig['http_javascripts_path'] = $this->httpJavascriptsPath;
  179. }
  180. // options in configuration file
  181. if (count($optionsConfig)) {
  182. $config = array();
  183. foreach ($this->plugins as $plugin) {
  184. $config[] = sprintf("require '%s'", addcslashes($plugin, '\\'));
  185. }
  186. foreach ($optionsConfig as $name => $value) {
  187. if (!is_array($value)) {
  188. $config[] = sprintf('%s = "%s"', $name, addcslashes($value, '\\'));
  189. } elseif (!empty($value)) {
  190. $config[] = sprintf('%s = %s', $name, $this->formatArrayToRuby($value));
  191. }
  192. }
  193. $configFile = tempnam($tempDir, 'assetic_compass');
  194. file_put_contents($configFile, implode("\n", $config)."\n");
  195. $pb->add('--config')->add($configFile);
  196. }
  197. $pb->add('--sass-dir')->add('')->add('--css-dir')->add('');
  198. // compass choose the type (sass or scss from the filename)
  199. if (null !== $this->scss) {
  200. $type = $this->scss ? 'scss' : 'sass';
  201. } elseif ($path) {
  202. // FIXME: what if the extension is something else?
  203. $type = pathinfo($path, PATHINFO_EXTENSION);
  204. } else {
  205. $type = 'scss';
  206. }
  207. $tempName = tempnam($tempDir, 'assetic_compass');
  208. unlink($tempName); // FIXME: don't use tempnam() here
  209. // input
  210. $pb->add($input = $tempName.'.'.$type);
  211. file_put_contents($input, $asset->getContent());
  212. // output
  213. $output = $tempName.'.css';
  214. // it's not really usefull but... https://github.com/chriseppstein/compass/issues/376
  215. $pb->setEnv('HOME', sys_get_temp_dir());
  216. $proc = $pb->getProcess();
  217. $code = $proc->run();
  218. if (0 < $code) {
  219. unlink($input);
  220. if (isset($configFile)) {
  221. unlink($configFile);
  222. }
  223. throw new \RuntimeException($proc->getErrorOutput() ?: $proc->getOutput());
  224. }
  225. $asset->setContent(file_get_contents($output));
  226. unlink($input);
  227. unlink($output);
  228. if (isset($configFile)) {
  229. unlink($configFile);
  230. }
  231. }
  232. public function filterDump(AssetInterface $asset)
  233. {
  234. }
  235. private function formatArrayToRuby($array)
  236. {
  237. $output = array();
  238. // does we have an associative array ?
  239. if (count(array_filter(array_keys($array), "is_numeric")) != count($array)) {
  240. foreach($array as $name => $value) {
  241. $output[] = sprintf(' :%s => "%s"', $name, addcslashes($value, '\\'));
  242. }
  243. $output = "{\n".implode(",\n", $output)."\n}";
  244. } else {
  245. foreach($array as $name => $value) {
  246. $output[] = sprintf(' "%s"', addcslashes($value, '\\'));
  247. }
  248. $output = "[\n".implode(",\n", $output)."\n]";
  249. }
  250. return $output;
  251. }
  252. }