1<?php 2 3namespace Drupal\Core\Asset; 4 5use Drupal\Component\Utility\NestedArray; 6use Drupal\Core\Asset\Exception\InvalidLibrariesExtendSpecificationException; 7use Drupal\Core\Asset\Exception\InvalidLibrariesOverrideSpecificationException; 8use Drupal\Core\Cache\CacheCollector; 9use Drupal\Core\Cache\CacheBackendInterface; 10use Drupal\Core\Lock\LockBackendInterface; 11use Drupal\Core\Theme\ThemeManagerInterface; 12 13/** 14 * A CacheCollector implementation for building library extension info. 15 */ 16class LibraryDiscoveryCollector extends CacheCollector { 17 18 /** 19 * The library discovery parser. 20 * 21 * @var \Drupal\Core\Asset\LibraryDiscoveryParser 22 */ 23 protected $discoveryParser; 24 25 /** 26 * The theme manager. 27 * 28 * @var \Drupal\Core\Theme\ThemeManagerInterface 29 */ 30 protected $themeManager; 31 32 /** 33 * Constructs a CacheCollector object. 34 * 35 * @param \Drupal\Core\Cache\CacheBackendInterface $cache 36 * The cache backend. 37 * @param \Drupal\Core\Lock\LockBackendInterface $lock 38 * The lock backend. 39 * @param \Drupal\Core\Asset\LibraryDiscoveryParser $discovery_parser 40 * The library discovery parser. 41 * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager 42 * The theme manager. 43 */ 44 public function __construct(CacheBackendInterface $cache, LockBackendInterface $lock, LibraryDiscoveryParser $discovery_parser, ThemeManagerInterface $theme_manager) { 45 $this->themeManager = $theme_manager; 46 parent::__construct(NULL, $cache, $lock, ['library_info']); 47 48 $this->discoveryParser = $discovery_parser; 49 } 50 51 /** 52 * {@inheritdoc} 53 */ 54 protected function getCid() { 55 if (!isset($this->cid)) { 56 $this->cid = 'library_info:' . $this->themeManager->getActiveTheme()->getName(); 57 } 58 59 return $this->cid; 60 } 61 62 /** 63 * {@inheritdoc} 64 */ 65 protected function resolveCacheMiss($key) { 66 $this->storage[$key] = $this->getLibraryDefinitions($key); 67 $this->persist($key); 68 69 return $this->storage[$key]; 70 } 71 72 /** 73 * Returns the library definitions for a given extension. 74 * 75 * This also implements libraries-overrides for entire libraries that have 76 * been specified by the LibraryDiscoveryParser. 77 * 78 * @param string $extension 79 * The name of the extension for which library definitions will be returned. 80 * 81 * @return array 82 * The library definitions for $extension with overrides applied. 83 * 84 * @throws \Drupal\Core\Asset\Exception\InvalidLibrariesOverrideSpecificationException 85 */ 86 protected function getLibraryDefinitions($extension) { 87 $libraries = $this->discoveryParser->buildByExtension($extension); 88 foreach ($libraries as $name => $definition) { 89 // Handle libraries that are marked for override or removal. 90 // @see \Drupal\Core\Asset\LibraryDiscoveryParser::applyLibrariesOverride() 91 if (isset($definition['override'])) { 92 if ($definition['override'] === FALSE) { 93 // Remove the library definition if FALSE is given. 94 unset($libraries[$name]); 95 } 96 else { 97 // Otherwise replace with existing library definition if it exists. 98 // Throw an exception if it doesn't. 99 list($replacement_extension, $replacement_name) = explode('/', $definition['override']); 100 $replacement_definition = $this->get($replacement_extension); 101 if (isset($replacement_definition[$replacement_name])) { 102 $libraries[$name] = $replacement_definition[$replacement_name]; 103 } 104 else { 105 throw new InvalidLibrariesOverrideSpecificationException(sprintf('The specified library %s does not exist.', $definition['override'])); 106 } 107 } 108 } 109 else { 110 // If libraries are not overridden, then apply libraries-extend. 111 $libraries[$name] = $this->applyLibrariesExtend($extension, $name, $definition); 112 } 113 } 114 return $libraries; 115 } 116 117 /** 118 * Applies the libraries-extend specified by the active theme. 119 * 120 * This extends the library definitions with the those specified by the 121 * libraries-extend specifications for the active theme. 122 * 123 * @param string $extension 124 * The name of the extension for which library definitions will be extended. 125 * @param string $library_name 126 * The name of the library whose definitions is to be extended. 127 * @param $library_definition 128 * The library definition to be extended. 129 * 130 * @return array 131 * The library definition extended as specified by libraries-extend. 132 * 133 * @throws \Drupal\Core\Asset\Exception\InvalidLibrariesExtendSpecificationException 134 */ 135 protected function applyLibrariesExtend($extension, $library_name, $library_definition) { 136 $libraries_extend = $this->themeManager->getActiveTheme()->getLibrariesExtend(); 137 if (!empty($libraries_extend["$extension/$library_name"])) { 138 foreach ($libraries_extend["$extension/$library_name"] as $library_extend_name) { 139 if (isset($library_definition['deprecated'])) { 140 $extend_message = sprintf('Theme "%s" is extending a deprecated library.', $extension); 141 $library_deprecation = str_replace('%library_id%', "$extension/$library_name", $library_definition['deprecated']); 142 @trigger_error("$extend_message $library_deprecation", E_USER_DEPRECATED); 143 } 144 if (!is_string($library_extend_name)) { 145 // Only string library names are allowed. 146 throw new InvalidLibrariesExtendSpecificationException('The libraries-extend specification for each library must be a list of strings.'); 147 } 148 list($new_extension, $new_library_name) = explode('/', $library_extend_name, 2); 149 $new_libraries = $this->get($new_extension); 150 if (isset($new_libraries[$new_library_name])) { 151 $library_definition = NestedArray::mergeDeep($library_definition, $new_libraries[$new_library_name]); 152 } 153 else { 154 throw new InvalidLibrariesExtendSpecificationException(sprintf('The specified library "%s" does not exist.', $library_extend_name)); 155 } 156 } 157 } 158 return $library_definition; 159 } 160 161 /** 162 * {@inheritdoc} 163 */ 164 public function reset() { 165 parent::reset(); 166 $this->cid = NULL; 167 } 168 169} 170