CssCollectionOptimizerLazy.php

Same filename and directory in other branches
  1. 10 core/lib/Drupal/Core/Asset/CssCollectionOptimizerLazy.php

Namespace

Drupal\Core\Asset

File

core/lib/Drupal/Core/Asset/CssCollectionOptimizerLazy.php

View source
<?php

namespace Drupal\Core\Asset;

use Drupal\Component\Datetime\TimeInterface;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Theme\ThemeManagerInterface;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * Optimizes CSS assets.
 */
class CssCollectionOptimizerLazy implements AssetCollectionGroupOptimizerInterface {
    use AssetGroupSetHashTrait;
    
    /**
     * Constructs a CssCollectionOptimizerLazy.
     *
     * @param \Drupal\Core\Asset\AssetCollectionGrouperInterface $grouper
     *   The grouper for CSS assets.
     * @param \Drupal\Core\Asset\AssetOptimizerInterface $optimizer
     *   The asset optimizer.
     * @param \Drupal\Core\Theme\ThemeManagerInterface $themeManager
     *   The theme manager.
     * @param \Drupal\Core\Asset\LibraryDependencyResolverInterface $dependencyResolver
     *   The library dependency resolver.
     * @param \Symfony\Component\HttpFoundation\RequestStack $requestStack
     *   The request stack.
     * @param \Drupal\Core\File\FileSystemInterface $fileSystem
     *   The file system service.
     * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
     *   The config factory.
     * @param \Drupal\Core\File\FileUrlGeneratorInterface $fileUrlGenerator
     *   The file URL generator.
     * @param \Drupal\Component\Datetime\TimeInterface $time
     *   The time service.
     * @param \Drupal\Core\Language\LanguageManagerInterface $languageManager
     *   The language manager.
     */
    public function __construct(AssetCollectionGrouperInterface $grouper, AssetOptimizerInterface $optimizer, ThemeManagerInterface $themeManager, LibraryDependencyResolverInterface $dependencyResolver, RequestStack $requestStack, FileSystemInterface $fileSystem, ConfigFactoryInterface $configFactory, FileUrlGeneratorInterface $fileUrlGenerator, TimeInterface $time, LanguageManagerInterface $languageManager) {
    }
    
    /**
     * {@inheritdoc}
     */
    public function optimize(array $css_assets, array $libraries) {
        // File names are generated based on library/asset definitions. This
        // includes a hash of the assets and the group index. Additionally, the full
        // set of libraries, already loaded libraries and theme are sent as query
        // parameters to allow a PHP controller to generate a valid file with
        // sufficient information. Files are not generated by this method since
        // they're assumed to be successfully returned from the URL created whether
        // on disk or not.
        // Group the assets.
        $css_groups = $this->grouper
            ->group($css_assets);
        $css_assets = [];
        foreach ($css_groups as $order => $css_group) {
            // We have to return a single asset, not a group of assets. It is now up
            // to one of the pieces of code in the switch statement below to set the
            // 'data' property to the appropriate value.
            $css_assets[$order] = $css_group;
            if ($css_group['type'] === 'file') {
                // No preprocessing, single CSS asset: just use the existing URI.
                if (!$css_group['preprocess']) {
                    $uri = $css_group['items'][0]['data'];
                    $css_assets[$order]['data'] = $uri;
                }
                else {
                    // To reproduce the full context of assets outside of the request,
                    // we must know the entire set of libraries used to generate all CSS
                    // groups, whether or not files in a group are from a particular
                    // library or not.
                    $css_assets[$order]['preprocessed'] = TRUE;
                }
            }
            if ($css_group['type'] === 'external') {
                // We don't do any aggregation and hence also no caching for external
                // CSS assets.
                $uri = $css_group['items'][0]['data'];
                $css_assets[$order]['data'] = $uri;
            }
        }
        // All asset group URLs will have exactly the same query arguments, except
        // for the delta, so prepare them in advance.
        $query_args = [
            'language' => $this->languageManager
                ->getCurrentLanguage()
                ->getId(),
            'theme' => $this->themeManager
                ->getActiveTheme()
                ->getName(),
            'include' => UrlHelper::compressQueryParameter(implode(',', $this->dependencyResolver
                ->getMinimalRepresentativeSubset($libraries))),
        ];
        $ajax_page_state = $this->requestStack
            ->getCurrentRequest()
            ->get('ajax_page_state');
        $already_loaded = isset($ajax_page_state) ? explode(',', $ajax_page_state['libraries']) : [];
        if ($already_loaded) {
            $query_args['exclude'] = UrlHelper::compressQueryParameter(implode(',', $this->dependencyResolver
                ->getMinimalRepresentativeSubset($already_loaded)));
        }
        // Generate a URL for each group of assets, but do not process them inline,
        // this is done using optimizeGroup() when the asset path is requested.
        foreach ($css_assets as $order => $css_asset) {
            if (!empty($css_asset['preprocessed'])) {
                $query = [
                    'delta' => "{$order}",
                ] + $query_args;
                $filename = 'css_' . $this->generateHash($css_asset) . '.css';
                $uri = 'assets://css/' . $filename;
                $css_assets[$order]['data'] = $this->fileUrlGenerator
                    ->generateString($uri) . '?' . UrlHelper::buildQuery($query);
            }
            unset($css_assets[$order]['items']);
        }
        return $css_assets;
    }
    
    /**
     * {@inheritdoc}
     */
    public function deleteAll() {
        $this->fileSystem
            ->deleteRecursive('assets://css');
    }
    
    /**
     * {@inheritdoc}
     */
    public function optimizeGroup(array $group) : string {
        // Optimize each asset within the group.
        $data = '';
        $current_license = FALSE;
        foreach ($group['items'] as $css_asset) {
            // Ensure license information is available as a comment after
            // optimization.
            if ($css_asset['license'] !== $current_license) {
                $data .= "/* @license " . $css_asset['license']['name'] . " " . $css_asset['license']['url'] . " */\n";
            }
            $current_license = $css_asset['license'];
            $data .= $this->optimizer
                ->optimize($css_asset);
        }
        // Per the W3C specification at
        // http://www.w3.org/TR/REC-CSS2/cascade.html#at-import, @import rules must
        // precede any other style, so we move those to the top. The regular
        // expression is expressed in NOWDOC since it is detecting backslashes as
        // well as single and double quotes. It is difficult to read when
        // represented as a quoted string.
        $regexp = <<<'REGEXP'
/@import\s*(?:'(?:\\'|.)*'|"(?:\\"|.)*"|url\(\s*(?:\\[\)\'\"]|[^'")])*\s*\)|url\(\s*'(?:\'|.)*'\s*\)|url\(\s*"(?:\"|.)*"\s*\)).*;/iU
REGEXP;
        preg_match_all($regexp, $data, $matches);
        $data = preg_replace($regexp, '', $data);
        return implode('', $matches[0]) . (!empty($matches[0]) ? "\n" : '') . $data;
    }

}

Classes

Title Deprecated Summary
CssCollectionOptimizerLazy Optimizes CSS assets.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.