23use InvalidArgumentException;
24use Wikimedia\Minify\CSSMin;
42 protected $origin = self::ORIGIN_CORE_SITEWIDE;
116 $this->localBasePath = static::extractLocalBasePath( $options,
$localBasePath );
118 $this->definition = $options;
125 if ( $this->definition ===
null ) {
130 $this->definition =
null;
132 if ( isset( $options[
'data'] ) ) {
134 $data = json_decode( file_get_contents( $dataPath ),
true );
135 $options = array_merge( $data, $options );
145 $prefix = isset( $options[
'prefix'] ) && $options[
'prefix'];
146 $selector = isset( $options[
'selector'] ) && $options[
'selector'];
148 && $options[
'selectorWithoutVariant'];
150 && $options[
'selectorWithVariant'];
153 throw new InvalidArgumentException(
154 "Given 'selectorWithoutVariant' but no 'selectorWithVariant'."
158 throw new InvalidArgumentException(
159 "Given 'selectorWithVariant' but no 'selectorWithoutVariant'."
163 throw new InvalidArgumentException(
164 "Incompatible 'selector' and 'selectorWithVariant'+'selectorWithoutVariant' given."
168 throw new InvalidArgumentException(
169 "None of 'prefix', 'selector' or 'selectorWithVariant'+'selectorWithoutVariant' given."
173 foreach ( $options as $member => $option ) {
177 if ( !is_array( $option ) ) {
178 throw new InvalidArgumentException(
179 "Invalid list error. '$option' given, array expected."
182 if ( !isset( $option[
'default'] ) ) {
184 $option = [
'default' => $option ];
186 foreach ( $option as $data ) {
187 if ( !is_array( $data ) ) {
188 throw new InvalidArgumentException(
189 "Invalid list error. '$data' given, array expected."
193 $this->{$member} = $option;
197 $this->{$member} = (bool)$option;
201 case 'selectorWithoutVariant':
202 case 'selectorWithVariant':
203 $this->{$member} = (string)$option;
207 $this->selectorWithoutVariant = $this->selectorWithVariant = (string)$option;
251 $skin = $context->getSkin();
252 if ( $this->imageObjects ===
null ) {
253 $this->loadFromDefinition();
254 $this->imageObjects = [];
256 if ( !isset( $this->imageObjects[$skin] ) ) {
257 $this->imageObjects[$skin] = [];
258 if ( !isset( $this->images[$skin] ) ) {
259 $this->images[$skin] = $this->images[
'default'] ?? [];
261 foreach ( $this->images[$skin] as $name => $options ) {
262 $fileDescriptor = is_array( $options ) ? $options[
'file'] : $options;
264 $allowedVariants = array_merge(
265 ( is_array( $options ) && isset( $options[
'variants'] ) ) ? $options[
'variants'] : [],
266 $this->getGlobalVariants( $context )
268 if ( isset( $this->variants[$skin] ) ) {
269 $variantConfig = array_intersect_key(
270 $this->variants[$skin],
271 array_fill_keys( $allowedVariants,
true )
281 $this->localBasePath,
285 $this->imageObjects[$skin][$image->getName()] = $image;
289 return $this->imageObjects[$skin];
299 $skin = $context->getSkin();
300 if ( $this->globalVariants ===
null ) {
301 $this->loadFromDefinition();
302 $this->globalVariants = [];
304 if ( !isset( $this->globalVariants[$skin] ) ) {
305 $this->globalVariants[$skin] = [];
306 if ( !isset( $this->variants[$skin] ) ) {
307 $this->variants[$skin] = $this->variants[
'default'] ?? [];
309 foreach ( $this->variants[$skin] as $name => $config ) {
310 if ( $config[
'global'] ??
false ) {
311 $this->globalVariants[$skin][] = $name;
316 return $this->globalVariants[$skin];
324 $this->loadFromDefinition();
329 $selectors = $this->getSelectors();
331 foreach ( $this->getImages( $context ) as $name => $image ) {
332 $declarations = $this->getStyleDeclarations( $context, $image, $script );
334 $selectors[
'selectorWithoutVariant'],
336 '{prefix}' => $this->getPrefix(),
341 $rules[] =
"$selector {\n\t$declarations\n}";
343 foreach ( $image->getVariants() as $variant ) {
344 $declarations = $this->getStyleDeclarations( $context, $image, $script, $variant );
346 $selectors[
'selectorWithVariant'],
348 '{prefix}' => $this->getPrefix(),
350 '{variant}' => $variant,
353 $rules[] =
"$selector {\n\t$declarations\n}";
357 $style = implode(
"\n", $rules );
358 return [
'all' => $style ];
372 private function getStyleDeclarations(
378 $imageDataUri = $this->useDataURI ? $image->getDataUri( $context, $variant,
'original' ) :
false;
379 $primaryUrl = $imageDataUri ?: $image->getUrl( $context, $script, $variant,
'original' );
380 $declarations = $this->getCssDeclarations(
382 $image->getUrl( $context, $script, $variant,
'rasterized' )
384 return implode(
"\n\t", $declarations );
396 $primaryUrl = CSSMin::buildUrlValue( $primary );
398 "background-image: $primaryUrl;",
416 $this->loadFromDefinition();
417 $summary = parent::getDefinitionSummary( $context );
425 'selectorWithoutVariant',
426 'selectorWithVariant',
428 $options[$member] = $this->{$member};
432 'options' => $options,
433 'fileHashes' => $this->getFileHashes( $context ),
443 private function getFileHashes(
Context $context ) {
444 $this->loadFromDefinition();
446 foreach ( $this->getImages( $context ) as $image ) {
447 $files[] = $image->getPath( $context );
449 $files = array_values( array_unique( $files ) );
450 return array_map( [ __CLASS__,
'safeFileHash' ], $files );
459 return $path->getLocalPath();
462 return "{$this->localBasePath}/$path";
476 if ( array_key_exists(
'localBasePath', $options ) ) {
477 $localBasePath = (string)$options[
'localBasePath'];
480 return $localBasePath ??
$IP;
487 return self::LOAD_STYLES;
492class_alias( ImageModule::class,
'ResourceLoaderImageModule' );
if(!defined( 'MEDIAWIKI')) if(ini_get('mbstring.func_overload')) if(!defined( 'MW_ENTRY_POINT')) global $IP
Environment checks.
Context object that contains information about the state of a specific ResourceLoader web request.