PageRenderTime 53ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

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

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