Mercurial > hg > isophonics-drupal-site
comparison core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | af1871eacc83 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\Core\Asset; | |
4 | |
5 use Drupal\Core\State\StateInterface; | |
6 | |
7 /** | |
8 * Optimizes CSS assets. | |
9 */ | |
10 class CssCollectionOptimizer implements AssetCollectionOptimizerInterface { | |
11 | |
12 /** | |
13 * A CSS asset grouper. | |
14 * | |
15 * @var \Drupal\Core\Asset\CssCollectionGrouper | |
16 */ | |
17 protected $grouper; | |
18 | |
19 /** | |
20 * A CSS asset optimizer. | |
21 * | |
22 * @var \Drupal\Core\Asset\CssOptimizer | |
23 */ | |
24 protected $optimizer; | |
25 | |
26 /** | |
27 * An asset dumper. | |
28 * | |
29 * @var \Drupal\Core\Asset\AssetDumper | |
30 */ | |
31 protected $dumper; | |
32 | |
33 /** | |
34 * The state key/value store. | |
35 * | |
36 * @var \Drupal\Core\State\StateInterface | |
37 */ | |
38 protected $state; | |
39 | |
40 /** | |
41 * Constructs a CssCollectionOptimizer. | |
42 * | |
43 * @param \Drupal\Core\Asset\AssetCollectionGrouperInterface $grouper | |
44 * The grouper for CSS assets. | |
45 * @param \Drupal\Core\Asset\AssetOptimizerInterface $optimizer | |
46 * The optimizer for a single CSS asset. | |
47 * @param \Drupal\Core\Asset\AssetDumperInterface $dumper | |
48 * The dumper for optimized CSS assets. | |
49 * @param \Drupal\Core\State\StateInterface $state | |
50 * The state key/value store. | |
51 */ | |
52 public function __construct(AssetCollectionGrouperInterface $grouper, AssetOptimizerInterface $optimizer, AssetDumperInterface $dumper, StateInterface $state) { | |
53 $this->grouper = $grouper; | |
54 $this->optimizer = $optimizer; | |
55 $this->dumper = $dumper; | |
56 $this->state = $state; | |
57 } | |
58 | |
59 /** | |
60 * {@inheritdoc} | |
61 * | |
62 * The cache file name is retrieved on a page load via a lookup variable that | |
63 * contains an associative array. The array key is the hash of the file names | |
64 * in $css while the value is the cache file name. The cache file is generated | |
65 * in two cases. First, if there is no file name value for the key, which will | |
66 * happen if a new file name has been added to $css or after the lookup | |
67 * variable is emptied to force a rebuild of the cache. Second, the cache file | |
68 * is generated if it is missing on disk. Old cache files are not deleted | |
69 * immediately when the lookup variable is emptied, but are deleted after a | |
70 * configurable period (@code system.performance.stale_file_threshold @endcode) | |
71 * to ensure that files referenced by a cached page will still be available. | |
72 */ | |
73 public function optimize(array $css_assets) { | |
74 // Group the assets. | |
75 $css_groups = $this->grouper->group($css_assets); | |
76 | |
77 // Now optimize (concatenate + minify) and dump each asset group, unless | |
78 // that was already done, in which case it should appear in | |
79 // drupal_css_cache_files. | |
80 // Drupal contrib can override this default CSS aggregator to keep the same | |
81 // grouping, optimizing and dumping, but change the strategy that is used to | |
82 // determine when the aggregate should be rebuilt (e.g. mtime, HTTPS …). | |
83 $map = $this->state->get('drupal_css_cache_files') ?: []; | |
84 $css_assets = []; | |
85 foreach ($css_groups as $order => $css_group) { | |
86 // We have to return a single asset, not a group of assets. It is now up | |
87 // to one of the pieces of code in the switch statement below to set the | |
88 // 'data' property to the appropriate value. | |
89 $css_assets[$order] = $css_group; | |
90 unset($css_assets[$order]['items']); | |
91 | |
92 switch ($css_group['type']) { | |
93 case 'file': | |
94 // No preprocessing, single CSS asset: just use the existing URI. | |
95 if (!$css_group['preprocess']) { | |
96 $uri = $css_group['items'][0]['data']; | |
97 $css_assets[$order]['data'] = $uri; | |
98 } | |
99 // Preprocess (aggregate), unless the aggregate file already exists. | |
100 else { | |
101 $key = $this->generateHash($css_group); | |
102 $uri = ''; | |
103 if (isset($map[$key])) { | |
104 $uri = $map[$key]; | |
105 } | |
106 if (empty($uri) || !file_exists($uri)) { | |
107 // Optimize each asset within the group. | |
108 $data = ''; | |
109 foreach ($css_group['items'] as $css_asset) { | |
110 $data .= $this->optimizer->optimize($css_asset); | |
111 } | |
112 // Per the W3C specification at | |
113 // http://www.w3.org/TR/REC-CSS2/cascade.html#at-import, @import | |
114 // rules must precede any other style, so we move those to the | |
115 // top. | |
116 $regexp = '/@import[^;]+;/i'; | |
117 preg_match_all($regexp, $data, $matches); | |
118 $data = preg_replace($regexp, '', $data); | |
119 $data = implode('', $matches[0]) . $data; | |
120 // Dump the optimized CSS for this group into an aggregate file. | |
121 $uri = $this->dumper->dump($data, 'css'); | |
122 // Set the URI for this group's aggregate file. | |
123 $css_assets[$order]['data'] = $uri; | |
124 // Persist the URI for this aggregate file. | |
125 $map[$key] = $uri; | |
126 $this->state->set('drupal_css_cache_files', $map); | |
127 } | |
128 else { | |
129 // Use the persisted URI for the optimized CSS file. | |
130 $css_assets[$order]['data'] = $uri; | |
131 } | |
132 $css_assets[$order]['preprocessed'] = TRUE; | |
133 } | |
134 break; | |
135 | |
136 case 'external': | |
137 // We don't do any aggregation and hence also no caching for external | |
138 // CSS assets. | |
139 $uri = $css_group['items'][0]['data']; | |
140 $css_assets[$order]['data'] = $uri; | |
141 break; | |
142 } | |
143 } | |
144 | |
145 return $css_assets; | |
146 } | |
147 | |
148 /** | |
149 * Generate a hash for a given group of CSS assets. | |
150 * | |
151 * @param array $css_group | |
152 * A group of CSS assets. | |
153 * | |
154 * @return string | |
155 * A hash to uniquely identify the given group of CSS assets. | |
156 */ | |
157 protected function generateHash(array $css_group) { | |
158 $css_data = []; | |
159 foreach ($css_group['items'] as $css_file) { | |
160 $css_data[] = $css_file['data']; | |
161 } | |
162 return hash('sha256', serialize($css_data)); | |
163 } | |
164 | |
165 /** | |
166 * {@inheritdoc} | |
167 */ | |
168 public function getAll() { | |
169 return $this->state->get('drupal_css_cache_files'); | |
170 } | |
171 | |
172 /** | |
173 * {@inheritdoc} | |
174 */ | |
175 public function deleteAll() { | |
176 $this->state->delete('drupal_css_cache_files'); | |
177 | |
178 $delete_stale = function ($uri) { | |
179 // Default stale file threshold is 30 days. | |
180 if (REQUEST_TIME - filemtime($uri) > \Drupal::config('system.performance')->get('stale_file_threshold')) { | |
181 file_unmanaged_delete($uri); | |
182 } | |
183 }; | |
184 file_scan_directory('public://css', '/.*/', ['callback' => $delete_stale]); | |
185 } | |
186 | |
187 } |