PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.2.3
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.2.3
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
wp-optimize / vendor / kriswallsmith / assetic / src / Assetic / Factory / AssetFactory.php

AssetFactory.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 3.2.3, at vendor/kriswallsmith/assetic/src/Assetic/Factory/AssetFactory.php

425 lines 12.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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
12 namespace Assetic\Factory;
13
14 use Assetic\Asset\AssetCollection;
15 use Assetic\Asset\AssetCollectionInterface;
16 use Assetic\Asset\AssetInterface;
17 use Assetic\Asset\AssetReference;
18 use Assetic\Asset\FileAsset;
19 use Assetic\Asset\GlobAsset;
20 use Assetic\Asset\HttpAsset;
21 use Assetic\AssetManager;
22 use Assetic\Factory\Worker\WorkerInterface;
23 use Assetic\Filter\DependencyExtractorInterface;
24 use Assetic\FilterManager;
25
26 /**
27 * The asset factory creates asset objects.
28 *
29 * @author Kris Wallsmith <[email protected]>
30 */
31 class AssetFactory
32 {
33 private $root;
34 private $debug;
35 private $output;
36 private $workers;
37 private $am;
38 private $fm;
39
40 /**
41 * Constructor.
42 *
43 * @param string $root The default root directory
44 * @param Boolean $debug Filters prefixed with a "?" will be omitted in debug mode
45 */
46 public function __construct($root, $debug = false)
47 {
48 $this->root = rtrim($root, '/');
49 $this->debug = $debug;
50 $this->output = 'assetic/*';
51 $this->workers = array();
52 }
53
54 /**
55 * Sets debug mode for the current factory.
56 *
57 * @param Boolean $debug Debug mode
58 */
59 public function setDebug($debug)
60 {
61 $this->debug = $debug;
62 }
63
64 /**
65 * Checks if the factory is in debug mode.
66 *
67 * @return Boolean Debug mode
68 */
69 public function isDebug()
70 {
71 return $this->debug;
72 }
73
74 /**
75 * Sets the default output string.
76 *
77 * @param string $output The default output string
78 */
79 public function setDefaultOutput($output)
80 {
81 $this->output = $output;
82 }
83
84 /**
85 * Adds a factory worker.
86 *
87 * @param WorkerInterface $worker A worker
88 */
89 public function addWorker(WorkerInterface $worker)
90 {
91 $this->workers[] = $worker;
92 }
93
94 /**
95 * Returns the current asset manager.
96 *
97 * @return AssetManager|null The asset manager
98 */
99 public function getAssetManager()
100 {
101 return $this->am;
102 }
103
104 /**
105 * Sets the asset manager to use when creating asset references.
106 *
107 * @param AssetManager $am The asset manager
108 */
109 public function setAssetManager(AssetManager $am)
110 {
111 $this->am = $am;
112 }
113
114 /**
115 * Returns the current filter manager.
116 *
117 * @return FilterManager|null The filter manager
118 */
119 public function getFilterManager()
120 {
121 return $this->fm;
122 }
123
124 /**
125 * Sets the filter manager to use when adding filters.
126 *
127 * @param FilterManager $fm The filter manager
128 */
129 public function setFilterManager(FilterManager $fm)
130 {
131 $this->fm = $fm;
132 }
133
134 /**
135 * Creates a new asset.
136 *
137 * Prefixing a filter name with a question mark will cause it to be
138 * omitted when the factory is in debug mode.
139 *
140 * Available options:
141 *
142 * * output: An output string
143 * * name: An asset name for interpolation in output patterns
144 * * debug: Forces debug mode on or off for this asset
145 * * root: An array or string of more root directories
146 *
147 * @param array|string $inputs An array of input strings
148 * @param array|string $filters An array of filter names
149 * @param array $options An array of options
150 *
151 * @return AssetCollection An asset collection
152 */
153 public function createAsset($inputs = array(), $filters = array(), array $options = array())
154 {
155 if (!is_array($inputs)) {
156 $inputs = array($inputs);
157 }
158
159 if (!is_array($filters)) {
160 $filters = array($filters);
161 }
162
163 if (!isset($options['output'])) {
164 $options['output'] = $this->output;
165 }
166
167 if (!isset($options['vars'])) {
168 $options['vars'] = array();
169 }
170
171 if (!isset($options['debug'])) {
172 $options['debug'] = $this->debug;
173 }
174
175 if (!isset($options['root'])) {
176 $options['root'] = array($this->root);
177 } else {
178 if (!is_array($options['root'])) {
179 $options['root'] = array($options['root']);
180 }
181
182 $options['root'][] = $this->root;
183 }
184
185 if (!isset($options['name'])) {
186 $options['name'] = $this->generateAssetName($inputs, $filters, $options);
187 }
188
189 $asset = $this->createAssetCollection(array(), $options);
190 $extensions = array();
191
192 // inner assets
193 foreach ($inputs as $input) {
194 if (is_array($input)) {
195 // nested formula
196 $asset->add(call_user_func_array(array($this, 'createAsset'), $input));
197 } else {
198 $asset->add($this->parseInput($input, $options));
199 $extensions[pathinfo($input, PATHINFO_EXTENSION)] = true;
200 }
201 }
202
203 // filters
204 foreach ($filters as $filter) {
205 if ('?' != $filter[0]) {
206 $asset->ensureFilter($this->getFilter($filter));
207 } elseif (!$options['debug']) {
208 $asset->ensureFilter($this->getFilter(substr($filter, 1)));
209 }
210 }
211
212 // append variables
213 if (!empty($options['vars'])) {
214 $toAdd = array();
215 foreach ($options['vars'] as $var) {
216 if (false !== strpos($options['output'], '{'.$var.'}')) {
217 continue;
218 }
219
220 $toAdd[] = '{'.$var.'}';
221 }
222
223 if ($toAdd) {
224 $options['output'] = str_replace('*', '*.'.implode('.', $toAdd), $options['output']);
225 }
226 }
227
228 // append consensus extension if missing
229 if (1 == count($extensions) && !pathinfo($options['output'], PATHINFO_EXTENSION) && $extension = key($extensions)) {
230 $options['output'] .= '.'.$extension;
231 }
232
233 // output --> target url
234 $asset->setTargetPath(str_replace('*', $options['name'], $options['output']));
235
236 // apply workers and return
237 return $this->applyWorkers($asset);
238 }
239
240 public function generateAssetName($inputs, $filters, $options = array())
241 {
242 foreach (array_diff(array_keys($options), array('output', 'debug', 'root')) as $key) {
243 unset($options[$key]);
244 }
245
246 ksort($options);
247
248 return substr(sha1(serialize($inputs).serialize($filters).serialize($options)), 0, 7);
249 }
250
251 public function getLastModified(AssetInterface $asset)
252 {
253 $mtime = 0;
254 foreach ($asset instanceof AssetCollectionInterface ? $asset : array($asset) as $leaf) {
255 $mtime = max($mtime, $leaf->getLastModified());
256
257 if (!$filters = $leaf->getFilters()) {
258 continue;
259 }
260
261 $prevFilters = array();
262 foreach ($filters as $filter) {
263 $prevFilters[] = $filter;
264
265 if (!$filter instanceof DependencyExtractorInterface) {
266 continue;
267 }
268
269 // extract children from leaf after running all preceeding filters
270 $clone = clone $leaf;
271 $clone->clearFilters();
272 foreach (array_slice($prevFilters, 0, -1) as $prevFilter) {
273 $clone->ensureFilter($prevFilter);
274 }
275 $clone->load();
276
277 foreach ($filter->getChildren($this, $clone->getContent(), $clone->getSourceDirectory()) as $child) {
278 $mtime = max($mtime, $this->getLastModified($child));
279 }
280 }
281 }
282
283 return $mtime;
284 }
285
286 /**
287 * Parses an input string string into an asset.
288 *
289 * The input string can be one of the following:
290 *
291 * * A reference: If the string starts with an "at" sign it will be interpreted as a reference to an asset in the asset manager
292 * * An absolute URL: If the string contains "://" or starts with "//" it will be interpreted as an HTTP asset
293 * * A glob: If the string contains a "*" it will be interpreted as a glob
294 * * A path: Otherwise the string is interpreted as a filesystem path
295 *
296 * Both globs and paths will be absolutized using the current root directory.
297 *
298 * @param string $input An input string
299 * @param array $options An array of options
300 *
301 * @return AssetInterface An asset
302 */
303 protected function parseInput($input, array $options = array())
304 {
305 if ('@' == $input[0]) {
306 return $this->createAssetReference(substr($input, 1));
307 }
308
309 if (false !== strpos($input, '://') || 0 === strpos($input, '//')) {
310 return $this->createHttpAsset($input, $options['vars']);
311 }
312
313 if (self::isAbsolutePath($input)) {
314 if ($root = self::findRootDir($input, $options['root'])) {
315 $path = ltrim(substr($input, strlen($root)), '/');
316 } else {
317 $path = null;
318 }
319 } else {
320 $root = $this->root;
321 $path = $input;
322 $input = $this->root.'/'.$path;
323 }
324
325 if (false !== strpos($input, '*')) {
326 return $this->createGlobAsset($input, $root, $options['vars']);
327 }
328
329 return $this->createFileAsset($input, $root, $path, $options['vars']);
330 }
331
332 protected function createAssetCollection(array $assets = array(), array $options = array())
333 {
334 return new AssetCollection($assets, array(), null, isset($options['vars']) ? $options['vars'] : array());
335 }
336
337 protected function createAssetReference($name)
338 {
339 if (!$this->am) {
340 throw new \LogicException('There is no asset manager.');
341 }
342
343 return new AssetReference($this->am, $name);
344 }
345
346 protected function createHttpAsset($sourceUrl, $vars)
347 {
348 return new HttpAsset($sourceUrl, array(), false, $vars);
349 }
350
351 protected function createGlobAsset($glob, $root = null, $vars)
352 {
353 return new GlobAsset($glob, array(), $root, $vars);
354 }
355
356 protected function createFileAsset($source, $root = null, $path = null, $vars)
357 {
358 return new FileAsset($source, array(), $root, $path, $vars);
359 }
360
361 protected function getFilter($name)
362 {
363 if (!$this->fm) {
364 throw new \LogicException('There is no filter manager.');
365 }
366
367 return $this->fm->get($name);
368 }
369
370 /**
371 * Filters an asset collection through the factory workers.
372 *
373 * Each leaf asset will be processed first, followed by the asset
374 * collection itself.
375 *
376 * @param AssetCollectionInterface $asset An asset collection
377 *
378 * @return AssetCollectionInterface
379 */
380 private function applyWorkers(AssetCollectionInterface $asset)
381 {
382 foreach ($asset as $leaf) {
383 foreach ($this->workers as $worker) {
384 $retval = $worker->process($leaf, $this);
385
386 if ($retval instanceof AssetInterface && $leaf !== $retval) {
387 $asset->replaceLeaf($leaf, $retval);
388 }
389 }
390 }
391
392 foreach ($this->workers as $worker) {
393 $retval = $worker->process($asset, $this);
394
395 if ($retval instanceof AssetInterface) {
396 $asset = $retval;
397 }
398 }
399
400 return $asset instanceof AssetCollectionInterface ? $asset : $this->createAssetCollection(array($asset));
401 }
402
403 private static function isAbsolutePath($path)
404 {
405 return '/' == $path[0] || '\\' == $path[0] || (3 < strlen($path) && ctype_alpha($path[0]) && $path[1] == ':' && ('\\' == $path[2] || '/' == $path[2]));
406 }
407
408 /**
409 * Loops through the root directories and returns the first match.
410 *
411 * @param string $path An absolute path
412 * @param array $roots An array of root directories
413 *
414 * @return string|null The matching root directory, if found
415 */
416 private static function findRootDir($path, array $roots)
417 {
418 foreach ($roots as $root) {
419 if (0 === strpos($path, $root)) {
420 return $root;
421 }
422 }
423 }
424 }
425