Yaffs site version 1.1
[yaffs-website] / web / modules / contrib / advagg / advagg_css_minify / src / Asset / CssOptimizer.php
1 <?php
2
3 namespace Drupal\advagg_css_minify\Asset;
4
5 use Drupal\Component\Utility\Unicode;
6 use Drupal\Core\Asset\AssetOptimizerInterface;
7 use Drupal\Core\Asset\CssOptimizer as CoreCssOptimizer;
8 use Drupal\Core\Cache\CacheBackendInterface;
9 use Drupal\Core\Config\ConfigFactoryInterface;
10 use Drupal\Core\Extension\ModuleHandlerInterface;
11 use Drupal\Core\State\StateInterface;
12
13 /**
14  * Optimizes a CSS asset.
15  */
16 class CssOptimizer extends CoreCssOptimizer implements AssetOptimizerInterface {
17
18   /**
19    * The minify cache.
20    *
21    * @var \Drupal\Core\Cache\CacheBackendInterface
22    */
23   protected $cache;
24
25   /**
26    * A config object for the advagg css minify configuration.
27    *
28    * @var \Drupal\Core\Config\Config
29    */
30   protected $config;
31
32   /**
33    * A config object for the advagg configuration.
34    *
35    * @var \Drupal\Core\Config\Config
36    */
37   protected $advaggConfig;
38
39   /**
40    * The AdvAgg file status state information storage service.
41    *
42    * @var \Drupal\Core\State\StateInterface
43    */
44   protected $advaggFiles;
45
46   /**
47    * Module handler service.
48    *
49    * @var \Drupal\Core\Extension\ModuleHandlerInterface
50    */
51   protected $moduleHandler;
52
53   /**
54    * Construct the optimizer instance.
55    *
56    * @param \Drupal\Core\Cache\CacheBackendInterface $minify_cache
57    *   The minify cache.
58    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
59    *   A config factory for retrieving required config objects.
60    * @param \Drupal\Core\State\StateInterface $advagg_files
61    *   The AdvAgg file status state information storage service.
62    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
63    *   The module handler.
64    */
65   public function __construct(CacheBackendInterface $minify_cache, ConfigFactoryInterface $config_factory, StateInterface $advagg_files, ModuleHandlerInterface $module_handler) {
66     $this->cache = $minify_cache;
67     $this->config = $config_factory->get('advagg_css_minify.settings');
68     $this->advaggConfig = $config_factory->get('advagg.settings');
69     $this->advaggFiles = $advagg_files;
70     $this->moduleHandler = $module_handler;
71   }
72
73   /**
74    * Generate the css minification configuration.
75    *
76    * @return array
77    *   Array($options, $description, $minifiers, $functions).
78    */
79   public function getConfiguration() {
80     $description = '';
81     $options = [
82       0 => t('Disabled'),
83       1 => t('Core'),
84       2 => t('YUI'),
85     ];
86
87     $minifiers = [NULL, NULL, NULL];
88     $functions = [NULL, NULL, NULL];
89
90     // Allow for other modules to alter this list.
91     $options_desc = [$options, $description];
92     $this->moduleHandler->alter('advagg_css_minify_configuration', $options_desc, $minifiers, $functions);
93     list($options, $description) = $options_desc;
94
95     return [$options, $description, $minifiers, $functions];
96   }
97
98   /**
99    * Loads the stylesheet and resolves all @import commands.
100    *
101    * Loads a stylesheet and replaces @import commands with the contents of the
102    * imported file. Use this instead of file_get_contents when processing
103    * stylesheets.
104    *
105    * The returned contents are compressed removing white space and comments only
106    * when CSS aggregation is enabled. This optimization will not apply for
107    * color.module enabled themes with CSS aggregation turned off.
108    *
109    * Note: the only reason this method is public is so color.module can call it;
110    * it is not on the AssetOptimizerInterface, so future refactorings can make
111    * it protected.
112    *
113    * @param string $file
114    *   Name of the stylesheet to be processed.
115    * @param bool $optimize
116    *   (optional) Defines if CSS contents should be compressed or not. Not used
117    *   in AdvAgg implementation.
118    * @param bool $reset_basepath
119    *   (optional) Used internally to facilitate recursive resolution of @import
120    *   commands.
121    *
122    * @return string
123    *   Contents of the stylesheet, including any resolved @import commands.
124    */
125   public function loadFile($file, $optimize = NULL, $reset_basepath = TRUE) {
126     // These statics are not cache variables, so we don't use drupal_static().
127     static $basepath;
128     if ($reset_basepath) {
129       $basepath = '';
130     }
131
132     // Stylesheets are relative one to each other. Start by adding a base path
133     // prefix provided by the parent stylesheet (if necessary).
134     if ($basepath && !file_uri_scheme($file)) {
135       $file = $basepath . '/' . $file;
136     }
137     // Store the parent base path to restore it later.
138     $parent_base_path = $basepath;
139     // Set the current base path to process possible child imports.
140     $basepath = dirname($file);
141
142     // Load the CSS stylesheet. We suppress errors because themes may specify
143     // stylesheets in their .info.yml file that don't exist in the theme's path,
144     // but are merely there to disable certain module CSS files.
145     $content = '';
146     if ($contents = @file_get_contents($file)) {
147       // If a BOM is found, convert the file to UTF-8, then use substr() to
148       // remove the BOM from the result.
149       if ($encoding = (Unicode::encodingFromBOM($contents))) {
150         $contents = Unicode::substr(Unicode::convertToUtf8($contents, $encoding), 1);
151       }
152       // If no BOM, check for fallback encoding. Per CSS spec the regex is very
153       // strict.
154       elseif (preg_match('/^@charset "([^"]+)";/', $contents, $matches)) {
155         if ($matches[1] !== 'utf-8' && $matches[1] !== 'UTF-8') {
156           $contents = substr($contents, strlen($matches[0]));
157           $contents = Unicode::convertToUtf8($contents, $matches[1]);
158         }
159       }
160
161       $minifier = $this->config->get('minifier');
162       if ($file_settings = $this->config->get('file_settings')) {
163         $file_settings = array_column($file_settings, 'minifier', 'path');
164         if (isset($file_settings[$file])) {
165           $minifier = $file_settings[$file];
166         }
167       }
168
169       $info = $this->advaggFiles->get($file);
170       $cid = 'css_minify:' . $minifier . ':' . $info['filename_hash'];
171       $cid .= !empty($info['content_hash']) ? ':' . $info['content_hash'] : '';
172       $cached_data = $this->cache->get($cid);
173       if (!empty($cached_data->data)) {
174         $content = $cached_data->data;
175       }
176       else {
177         if (!$minifier || $this->advaggConfig->get('cache_level') < 0) {
178           $content = $this->processCss($contents, FALSE);
179         }
180         elseif ($minifier == 1) {
181           $content = $this->processCss($contents, TRUE);
182         }
183         elseif ($minifier == 2) {
184           $content = $this->processCssMin($contents);
185         }
186         else {
187           $content = $this->processCssOther($contents, $minifier);
188         }
189       }
190       // Cache minified data for at least 1 week.
191       $this->cache->set($cid, $content, REQUEST_TIME + (86400 * 7), ['advagg_css', $info['filename_hash']]);
192     }
193
194     // Restore the parent base path as the file and its children are processed.
195     $basepath = $parent_base_path;
196     return $content;
197   }
198
199   /**
200    * Processes the contents of a stylesheet through CSSMin for minification.
201    *
202    * @param string $contents
203    *   The contents of the stylesheet.
204    *
205    * @return string
206    *   Minified contents of the stylesheet including the imported stylesheets.
207    */
208   protected function processCssMin($contents) {
209     $contents = $this->clean($contents);
210     if (!class_exists('CSSmin')) {
211       include drupal_get_path('module', 'advagg_css_minify') . '/yui/CSSMin.inc';
212     }
213     $cssmin = new \CSSmin(TRUE);
214
215     // Minify the CSS splitting lines after 4k of text.
216     $contents = $cssmin->run($contents, 4096);
217
218     // Replaces @import commands with the actual stylesheet content.
219     // This happens recursively but omits external files.
220     $contents = preg_replace_callback('/@import\s*(?:url\(\s*)?[\'"]?(?![a-z]+:)(?!\/\/)([^\'"\()]+)[\'"]?\s*\)?\s*;/', [$this, 'loadNestedFile'], $contents);
221
222     return $contents;
223   }
224
225   /**
226    * Processes the contents of a stylesheet for minification.
227    *
228    * @param string $contents
229    *   The contents of the stylesheet.
230    * @param string $minifier
231    *   The name of the minifier to use.
232    *
233    * @return string
234    *   Minified contents of the stylesheet including the imported stylesheets.
235    */
236   protected function processCssOther($contents, $minifier) {
237     $contents = $this->clean($contents);
238     list(, , , $functions) = $this->getConfiguration();
239     if (isset($functions[$minifier])) {
240       $run = $functions[$minifier];
241       if (function_exists($run)) {
242         $run($contents);
243       }
244     }
245     // Replaces @import commands with the actual stylesheet content.
246     // This happens recursively but omits external files.
247     $contents = preg_replace_callback('/@import\s*(?:url\(\s*)?[\'"]?(?![a-z]+:)(?!\/\/)([^\'"\()]+)[\'"]?\s*\)?\s*;/', [$this, 'loadNestedFile'], $contents);
248
249     return $contents;
250   }
251
252 }