PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/kriswallsmith/assetic/src/Assetic/Factory/AssetFactory.php

https://bitbucket.org/pyneff/symfony2
PHP | 386 lines | 216 code | 51 blank | 119 comment | 41 complexity | b73b557466558d5d3fce845c2d141d66 MD5 | raw file
Possible License(s): Apache-2.0, LGPL-3.0, BSD-3-Clause, BSD-2-Clause
  1. <?php
  2. /*
  3. * This file is part of the Assetic package, an OpenSky project.
  4. *
  5. * (c) 2010-2012 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\Factory;
  11. use Assetic\Asset\AssetCollection;
  12. use Assetic\Asset\AssetCollectionInterface;
  13. use Assetic\Asset\AssetInterface;
  14. use Assetic\Asset\AssetReference;
  15. use Assetic\Asset\FileAsset;
  16. use Assetic\Asset\GlobAsset;
  17. use Assetic\Asset\HttpAsset;
  18. use Assetic\AssetManager;
  19. use Assetic\Factory\Worker\WorkerInterface;
  20. use Assetic\FilterManager;
  21. /**
  22. * The asset factory creates asset objects.
  23. *
  24. * @author Kris Wallsmith <kris.wallsmith@gmail.com>
  25. */
  26. class AssetFactory
  27. {
  28. private $root;
  29. private $debug;
  30. private $output;
  31. private $workers;
  32. private $am;
  33. private $fm;
  34. /**
  35. * Constructor.
  36. *
  37. * @param string $root The default root directory
  38. * @param string $output The default output string
  39. * @param Boolean $debug Filters prefixed with a "?" will be omitted in debug mode
  40. */
  41. public function __construct($root, $debug = false)
  42. {
  43. $this->root = rtrim($root, '/');
  44. $this->debug = $debug;
  45. $this->output = 'assetic/*';
  46. $this->workers = array();
  47. }
  48. /**
  49. * Sets debug mode for the current factory.
  50. *
  51. * @param Boolean $debug Debug mode
  52. */
  53. public function setDebug($debug)
  54. {
  55. $this->debug = $debug;
  56. }
  57. /**
  58. * Checks if the factory is in debug mode.
  59. *
  60. * @return Boolean Debug mode
  61. */
  62. public function isDebug()
  63. {
  64. return $this->debug;
  65. }
  66. /**
  67. * Sets the default output string.
  68. *
  69. * @param string $output The default output string
  70. */
  71. public function setDefaultOutput($output)
  72. {
  73. $this->output = $output;
  74. }
  75. /**
  76. * Adds a factory worker.
  77. *
  78. * @param WorkerInterface $worker A worker
  79. */
  80. public function addWorker(WorkerInterface $worker)
  81. {
  82. $this->workers[] = $worker;
  83. }
  84. /**
  85. * Returns the current asset manager.
  86. *
  87. * @return AssetManager|null The asset manager
  88. */
  89. public function getAssetManager()
  90. {
  91. return $this->am;
  92. }
  93. /**
  94. * Sets the asset manager to use when creating asset references.
  95. *
  96. * @param AssetManager $am The asset manager
  97. */
  98. public function setAssetManager(AssetManager $am)
  99. {
  100. $this->am = $am;
  101. }
  102. /**
  103. * Returns the current filter manager.
  104. *
  105. * @return FilterManager|null The filter manager
  106. */
  107. public function getFilterManager()
  108. {
  109. return $this->fm;
  110. }
  111. /**
  112. * Sets the filter manager to use when adding filters.
  113. *
  114. * @param FilterManager $fm The filter manager
  115. */
  116. public function setFilterManager(FilterManager $fm)
  117. {
  118. $this->fm = $fm;
  119. }
  120. /**
  121. * Creates a new asset.
  122. *
  123. * Prefixing a filter name with a question mark will cause it to be
  124. * omitted when the factory is in debug mode.
  125. *
  126. * Available options:
  127. *
  128. * * output: An output string
  129. * * name: An asset name for interpolation in output patterns
  130. * * debug: Forces debug mode on or off for this asset
  131. * * root: An array or string of more root directories
  132. *
  133. * @param array|string $inputs An array of input strings
  134. * @param array|string $filters An array of filter names
  135. * @param array $options An array of options
  136. *
  137. * @return AssetCollection An asset collection
  138. */
  139. public function createAsset($inputs = array(), $filters = array(), array $options = array())
  140. {
  141. if (!is_array($inputs)) {
  142. $inputs = array($inputs);
  143. }
  144. if (!is_array($filters)) {
  145. $filters = array($filters);
  146. }
  147. if (!isset($options['output'])) {
  148. $options['output'] = $this->output;
  149. }
  150. if (!isset($options['vars'])) {
  151. $options['vars'] = array();
  152. }
  153. if (!isset($options['debug'])) {
  154. $options['debug'] = $this->debug;
  155. }
  156. if (!isset($options['root'])) {
  157. $options['root'] = array($this->root);
  158. } else {
  159. if (!is_array($options['root'])) {
  160. $options['root'] = array($options['root']);
  161. }
  162. $options['root'][] = $this->root;
  163. }
  164. if (!isset($options['name'])) {
  165. $options['name'] = $this->generateAssetName($inputs, $filters, $options);
  166. }
  167. $asset = $this->createAssetCollection(array(), $options);
  168. $extensions = array();
  169. // inner assets
  170. foreach ($inputs as $input) {
  171. if (is_array($input)) {
  172. // nested formula
  173. $asset->add(call_user_func_array(array($this, 'createAsset'), $input));
  174. } else {
  175. $asset->add($this->parseInput($input, $options));
  176. $extensions[pathinfo($input, PATHINFO_EXTENSION)] = true;
  177. }
  178. }
  179. // filters
  180. foreach ($filters as $filter) {
  181. if ('?' != $filter[0]) {
  182. $asset->ensureFilter($this->getFilter($filter));
  183. } elseif (!$options['debug']) {
  184. $asset->ensureFilter($this->getFilter(substr($filter, 1)));
  185. }
  186. }
  187. // append variables
  188. if (!empty($options['vars'])) {
  189. $toAdd = array();
  190. foreach ($options['vars'] as $var) {
  191. if (false !== strpos($options['output'], '{'.$var.'}')) {
  192. continue;
  193. }
  194. $toAdd[] = '{'.$var.'}';
  195. }
  196. if ($toAdd) {
  197. $options['output'] = str_replace('*', '*.'.implode('.', $toAdd), $options['output']);
  198. }
  199. }
  200. // append consensus extension if missing
  201. if (1 == count($extensions) && !pathinfo($options['output'], PATHINFO_EXTENSION) && $extension = key($extensions)) {
  202. $options['output'] .= '.'.$extension;
  203. }
  204. // output --> target url
  205. $asset->setTargetPath(str_replace('*', $options['name'], $options['output']));
  206. // apply workers and return
  207. return $this->applyWorkers($asset);
  208. }
  209. public function generateAssetName($inputs, $filters, $options = array())
  210. {
  211. foreach (array_diff(array_keys($options), array('output', 'debug', 'root')) as $key) {
  212. unset($options[$key]);
  213. }
  214. ksort($options);
  215. return substr(sha1(serialize($inputs).serialize($filters).serialize($options)), 0, 7);
  216. }
  217. /**
  218. * Parses an input string string into an asset.
  219. *
  220. * The input string can be one of the following:
  221. *
  222. * * A reference: If the string starts with an "at" sign it will be interpreted as a reference to an asset in the asset manager
  223. * * An absolute URL: If the string contains "://" or starts with "//" it will be interpreted as an HTTP asset
  224. * * A glob: If the string contains a "*" it will be interpreted as a glob
  225. * * A path: Otherwise the string is interpreted as a filesystem path
  226. *
  227. * Both globs and paths will be absolutized using the current root directory.
  228. *
  229. * @param string $input An input string
  230. * @param array $options An array of options
  231. *
  232. * @return AssetInterface An asset
  233. */
  234. protected function parseInput($input, array $options = array())
  235. {
  236. if ('@' == $input[0]) {
  237. return $this->createAssetReference(substr($input, 1));
  238. }
  239. if (false !== strpos($input, '://') || 0 === strpos($input, '//')) {
  240. return $this->createHttpAsset($input, $options['vars']);
  241. }
  242. if (self::isAbsolutePath($input)) {
  243. if ($root = self::findRootDir($input, $options['root'])) {
  244. $path = ltrim(substr($input, strlen($root)), '/');
  245. } else {
  246. $path = null;
  247. }
  248. } else {
  249. $root = $this->root;
  250. $path = $input;
  251. $input = $this->root.'/'.$path;
  252. }
  253. if (false !== strpos($input, '*')) {
  254. return $this->createGlobAsset($input, $root, $options['vars']);
  255. } else {
  256. return $this->createFileAsset($input, $root, $path, $options['vars']);
  257. }
  258. }
  259. protected function createAssetCollection(array $assets = array(), array $options = array())
  260. {
  261. return new AssetCollection($assets, array(), null, isset($options['vars']) ? $options['vars'] : array());
  262. }
  263. protected function createAssetReference($name)
  264. {
  265. if (!$this->am) {
  266. throw new \LogicException('There is no asset manager.');
  267. }
  268. return new AssetReference($this->am, $name);
  269. }
  270. protected function createHttpAsset($sourceUrl, $vars)
  271. {
  272. return new HttpAsset($sourceUrl, array(), false, $vars);
  273. }
  274. protected function createGlobAsset($glob, $root = null, $vars)
  275. {
  276. return new GlobAsset($glob, array(), $root, $vars);
  277. }
  278. protected function createFileAsset($source, $root = null, $path = null, $vars)
  279. {
  280. return new FileAsset($source, array(), $root, $path, $vars);
  281. }
  282. protected function getFilter($name)
  283. {
  284. if (!$this->fm) {
  285. throw new \LogicException('There is no filter manager.');
  286. }
  287. return $this->fm->get($name);
  288. }
  289. /**
  290. * Filters an asset collection through the factory workers.
  291. *
  292. * Each leaf asset will be processed first, followed by the asset
  293. * collection itself.
  294. *
  295. * @param AssetCollectionInterface $asset An asset collection
  296. */
  297. private function applyWorkers(AssetCollectionInterface $asset)
  298. {
  299. foreach ($asset as $leaf) {
  300. foreach ($this->workers as $worker) {
  301. $retval = $worker->process($leaf);
  302. if ($retval instanceof AssetInterface && $leaf !== $retval) {
  303. $asset->replaceLeaf($leaf, $retval);
  304. }
  305. }
  306. }
  307. foreach ($this->workers as $worker) {
  308. $retval = $worker->process($asset);
  309. if ($retval instanceof AssetInterface) {
  310. $asset = $retval;
  311. }
  312. }
  313. return $asset instanceof AssetCollectionInterface ? $asset : $this->createAssetCollection(array($asset));
  314. }
  315. private static function isAbsolutePath($path)
  316. {
  317. return '/' == $path[0] || '\\' == $path[0] || (3 < strlen($path) && ctype_alpha($path[0]) && $path[1] == ':' && ('\\' == $path[2] || '/' == $path[2]));
  318. }
  319. /**
  320. * Loops through the root directories and returns the first match.
  321. *
  322. * @param string $path An absolute path
  323. * @param array $roots An array of root directories
  324. *
  325. * @return string|null The matching root directory, if found
  326. */
  327. private static function findRootDir($path, array $roots)
  328. {
  329. foreach ($roots as $root) {
  330. if (0 === strpos($path, $root)) {
  331. return $root;
  332. }
  333. }
  334. }
  335. }