Namespace

Drupal\advagg\Asset

Fichier

src/Asset/AssetResolver.php

View source
<?php

namespace Drupal\advagg\Asset;

use Drupal\Core\Asset\AssetCollectionOptimizerInterface;
use Drupal\Core\Asset\AssetResolver as CoreAssetResolver;
use Drupal\Core\Asset\AssetResolverInterface;
use Drupal\Core\Asset\AttachedAssetsInterface;
use Drupal\Core\Asset\LibraryDiscoveryInterface;
use Drupal\Core\Asset\LibraryDependencyResolverInterface;
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Theme\ThemeManagerInterface;
use Drupal\Core\State\StateInterface;

/**
 * The default asset resolver.
 */
class AssetResolver extends CoreAssetResolver implements AssetResolverInterface {
    
    /**
     * The CSS collection optimizer.
     *
     * @var \Drupal\Core\Asset\AssetCollectionOptimizerInterface
     */
    protected $cssCollectionOptimizer;
    
    /**
     * The JS collection optimizer.
     *
     * @var \Drupal\Core\Asset\AssetCollectionOptimizerInterface
     */
    protected $jsCollectionOptimizer;
    
    /**
     * The AdvAgg file status state information storage service.
     *
     * @var \Drupal\Core\State\StateInterface
     */
    protected $advaggFiles;
    
    /**
     * Constructs a new AssetResolver instance.
     *
     * @param \Drupal\Core\Asset\LibraryDiscoveryInterface $library_discovery
     *   The library discovery service.
     * @param \Drupal\Core\Asset\LibraryDependencyResolverInterface $library_dependency_resolver
     *   The library dependency resolver.
     * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
     *   The module handler.
     * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
     *   The theme manager.
     * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
     *   The language manager.
     * @param \Drupal\Core\Cache\CacheBackendInterface $cache
     *   The cache backend.
     * @param \Drupal\Core\Asset\AssetCollectionOptimizerInterface $css_collection_optimizer
     *   The CSS collection optimizer.
     * @param \Drupal\Core\Asset\AssetCollectionOptimizerInterface $js_collection_optimizer
     *   The JS collection optimizer.
     * @param \Drupal\Core\State\StateInterface $advagg_files
     *   The AdvAgg file status state information storage service.
     */
    public function __construct(LibraryDiscoveryInterface $library_discovery, LibraryDependencyResolverInterface $library_dependency_resolver, ModuleHandlerInterface $module_handler, ThemeManagerInterface $theme_manager, LanguageManagerInterface $language_manager, CacheBackendInterface $cache, AssetCollectionOptimizerInterface $css_collection_optimizer, AssetCollectionOptimizerInterface $js_collection_optimizer, StateInterface $advagg_files) {
        $this->libraryDiscovery = $library_discovery;
        $this->libraryDependencyResolver = $library_dependency_resolver;
        $this->moduleHandler = $module_handler;
        $this->themeManager = $theme_manager;
        $this->languageManager = $language_manager;
        $this->cache = $cache;
        $this->cssCollectionOptimizer = $css_collection_optimizer;
        $this->jsCollectionOptimizer = $js_collection_optimizer;
        $this->advaggFiles = $advagg_files;
    }
    
    /**
     * {@inheritdoc}
     */
    public function getCssAssets(AttachedAssetsInterface $assets, $optimize) {
        $theme_info = $this->themeManager
            ->getActiveTheme();
        // Add the theme name to the cache key since themes may implement
        // hook_css_alter().
        $libraries_to_load = $this->getLibrariesToLoad($assets);
        $cid = 'css:' . $theme_info->getName() . ':' . Crypt::hashBase64(serialize($libraries_to_load)) . (int) $optimize;
        if ($cached = $this->cache
            ->get($cid)) {
            return $cached->data;
        }
        $css = [];
        $default_options = [
            'type' => 'file',
            'group' => CSS_AGGREGATE_DEFAULT,
            'weight' => 0,
            'media' => 'all',
            'preprocess' => TRUE,
            'browsers' => [],
        ];
        foreach ($libraries_to_load as $library) {
            list($extension, $name) = explode('/', $library, 2);
            $definition = $this->libraryDiscovery
                ->getLibraryByName($extension, $name);
            if (isset($definition['css'])) {
                foreach ($definition['css'] as $options) {
                    $options += $default_options;
                    $options['browsers'] += [
                        'IE' => TRUE,
                        '!IE' => TRUE,
                    ];
                    // Files with a query string cannot be preprocessed.
                    if ($options['type'] === 'file' && $options['preprocess'] && strpos($options['data'], '?') !== FALSE) {
                        $options['preprocess'] = FALSE;
                    }
                    // Always add a tiny value to the weight, to conserve the insertion
                    // order.
                    $options['weight'] += count($css) / 1000;
                    // CSS files are being keyed by the full path.
                    $css[$options['data']] = $options;
                }
            }
        }
        // Allow modules and themes to alter the CSS assets.
        $this->moduleHandler
            ->alter('css', $css, $assets);
        $this->themeManager
            ->alter('css', $css, $assets);
        // After alter get file information (in case alter changes things).
        $this->advaggFiles
            ->getMultiple(array_column($css, 'data'));
        // Sort CSS items, so that they appear in the correct order.
        uasort($css, 'static::sort');
        // Allow themes to remove CSS files by CSS files full path and file name.
        // @todo Remove in Drupal 9.0.x.
        if ($stylesheet_remove = $theme_info->getStyleSheetsRemove()) {
            foreach ($css as $key => $options) {
                if (isset($stylesheet_remove[$key])) {
                    unset($css[$key]);
                }
            }
        }
        if ($optimize) {
            $css = $this->cssCollectionOptimizer
                ->optimize($css);
        }
        $this->cache
            ->set($cid, $css, CacheBackendInterface::CACHE_PERMANENT, [
            'library_info',
        ]);
        return $css;
    }
    
    /**
     * {@inheritdoc}
     */
    public function getJsAssets(AttachedAssetsInterface $assets, $optimize) {
        $theme_info = $this->themeManager
            ->getActiveTheme();
        // Add the theme name to the cache key since themes may implement
        // hook_js_alter(). Additionally add the current language to support
        // translation of JavaScript files.
        $libraries_to_load = $this->getLibrariesToLoad($assets);
        $cid = 'js:' . $theme_info->getName() . ':' . $this->languageManager
            ->getCurrentLanguage()
            ->getId() . ':' . Crypt::hashBase64(serialize($libraries_to_load)) . (int) (count($assets->getSettings()) > 0) . (int) $optimize;
        if ($cached = $this->cache
            ->get($cid)) {
            list($js_assets_header, $js_assets_footer, $settings, $settings_in_header) = $cached->data;
        }
        else {
            $javascript = [];
            $default_options = [
                'type' => 'file',
                'group' => JS_DEFAULT,
                'weight' => 0,
                'cache' => TRUE,
                'preprocess' => TRUE,
                'attributes' => [],
                'version' => NULL,
                'browsers' => [],
            ];
            // Collect all libraries that contain JS assets and are in the header.
            $header_js_libraries = [];
            foreach ($libraries_to_load as $library) {
                list($extension, $name) = explode('/', $library, 2);
                $definition = $this->libraryDiscovery
                    ->getLibraryByName($extension, $name);
                if (isset($definition['js']) && !empty($definition['header'])) {
                    $header_js_libraries[] = $library;
                }
            }
            // The current list of header JS libraries are only those libraries that
            // are in the header, but their dependencies must also be loaded for them
            // to function correctly, so update the list with those.
            $header_js_libraries = $this->libraryDependencyResolver
                ->getLibrariesWithDependencies($header_js_libraries);
            foreach ($libraries_to_load as $library) {
                list($extension, $name) = explode('/', $library, 2);
                $definition = $this->libraryDiscovery
                    ->getLibraryByName($extension, $name);
                if (isset($definition['js'])) {
                    foreach ($definition['js'] as $options) {
                        $options += $default_options;
                        // 'scope' is a calculated option, based on which libraries are
                        // marked to be loaded from the header (see above).
                        $options['scope'] = in_array($library, $header_js_libraries) ? 'header' : 'footer';
                        // Preprocess can only be set if caching is enabled and no
                        // attributes are set.
                        $options['preprocess'] = $options['cache'] && empty($options['attributes']) ? $options['preprocess'] : FALSE;
                        // Always add a tiny value to the weight, to conserve the insertion
                        // order.
                        $options['weight'] += count($javascript) / 1000;
                        // Local and external files must keep their name as the associative
                        // key so the same JavaScript file is not added twice.
                        $javascript[$options['data']] = $options;
                    }
                }
            }
            // Allow modules and themes to alter the JavaScript assets.
            $this->moduleHandler
                ->alter('js', $javascript, $assets);
            $this->themeManager
                ->alter('js', $javascript, $assets);
            // After alter get file information (in case alter changes things).
            $this->advaggFiles
                ->getMultiple(array_column($javascript, 'data'));
            // Sort JavaScript assets, so that they appear in the correct order.
            uasort($javascript, 'static::sort');
            // Prepare the return value: filter JavaScript assets per scope.
            $js_assets_header = [];
            $js_assets_footer = [];
            foreach ($javascript as $key => $item) {
                if ($item['scope'] == 'header') {
                    $js_assets_header[$key] = $item;
                }
                elseif ($item['scope'] == 'footer') {
                    $js_assets_footer[$key] = $item;
                }
            }
            if ($optimize) {
                $js_assets_header = $this->jsCollectionOptimizer
                    ->optimize($js_assets_header);
                $js_assets_footer = $this->jsCollectionOptimizer
                    ->optimize($js_assets_footer);
            }
            // If the core/drupalSettings library is being loaded or is already
            // loaded, get the JavaScript settings assets, and convert them into a
            // single "regular" JavaScript asset.
            $libraries_to_load = $this->getLibrariesToLoad($assets);
            $settings_required = in_array('core/drupalSettings', $libraries_to_load) || in_array('core/drupalSettings', $this->libraryDependencyResolver
                ->getLibrariesWithDependencies($assets->getAlreadyLoadedLibraries()));
            $settings_have_changed = count($libraries_to_load) > 0 || count($assets->getSettings()) > 0;
            // Initialize settings to FALSE since they are not needed by default. This
            // distinguishes between an empty array which must still allow
            // hook_js_settings_alter() to be run.
            $settings = FALSE;
            if ($settings_required && $settings_have_changed) {
                $settings = $this->getJsSettingsAssets($assets);
                // Allow modules to add cached JavaScript settings.
                foreach ($this->moduleHandler
                    ->getImplementations('js_settings_build') as $module) {
                    $function = $module . '_js_settings_build';
                    $function($settings, $assets);
                }
            }
            $settings_in_header = in_array('core/drupalSettings', $header_js_libraries);
            $this->cache
                ->set($cid, [
                $js_assets_header,
                $js_assets_footer,
                $settings,
                $settings_in_header,
            ], CacheBackendInterface::CACHE_PERMANENT, [
                'library_info',
            ]);
        }
        if ($settings !== FALSE) {
            // Attached settings override both library definitions and
            // hook_js_settings_build().
            $settings = NestedArray::mergeDeepArray([
                $settings,
                $assets->getSettings(),
            ], TRUE);
            // Allow modules and themes to alter the JavaScript settings.
            $this->moduleHandler
                ->alter('js_settings', $settings, $assets);
            $this->themeManager
                ->alter('js_settings', $settings, $assets);
            // Update the $assets object accordingly, so that it reflects the final
            // settings.
            $assets->setSettings($settings);
            $settings_as_inline_javascript = [
                'type' => 'setting',
                'group' => JS_SETTING,
                'weight' => 0,
                'browsers' => [],
                'data' => $settings,
            ];
            $settings_js_asset = [
                'drupalSettings' => $settings_as_inline_javascript,
            ];
            // Prepend to the list of JS assets, to render it first. Preferably in
            // the footer, but in the header if necessary.
            if ($settings_in_header) {
                $js_assets_header = $settings_js_asset + $js_assets_header;
            }
            else {
                $js_assets_footer = $settings_js_asset + $js_assets_footer;
            }
        }
        return [
            $js_assets_header,
            $js_assets_footer,
        ];
    }

}

Classes

Titre Deprecated Résumé
AssetResolver The default asset resolver.