ResourceLoaderContext::getImage()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Context for ResourceLoader modules.
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License along
16
 * with this program; if not, write to the Free Software Foundation, Inc.,
17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
 * http://www.gnu.org/copyleft/gpl.html
19
 *
20
 * @file
21
 * @author Trevor Parscal
22
 * @author Roan Kattouw
23
 */
24
25
use MediaWiki\Logger\LoggerFactory;
26
27
/**
28
 * Object passed around to modules which contains information about the state
29
 * of a specific loader request.
30
 */
31
class ResourceLoaderContext {
32
	protected $resourceLoader;
33
	protected $request;
34
	protected $logger;
35
36
	// Module content vary
37
	protected $skin;
38
	protected $language;
39
	protected $debug;
40
	protected $user;
41
42
	// Request vary (in addition to cache vary)
43
	protected $modules;
44
	protected $only;
45
	protected $version;
46
	protected $raw;
47
	protected $image;
48
	protected $variant;
49
	protected $format;
50
51
	protected $direction;
52
	protected $hash;
53
	protected $userObj;
54
	protected $imageObj;
55
56
	/**
57
	 * @param ResourceLoader $resourceLoader
58
	 * @param WebRequest $request
59
	 */
60
	public function __construct( ResourceLoader $resourceLoader, WebRequest $request ) {
61
		$this->resourceLoader = $resourceLoader;
62
		$this->request = $request;
63
		$this->logger = $resourceLoader->getLogger();
64
65
		// Future developers: Avoid use of getVal() in this class, which performs
66
		// expensive UTF normalisation by default. Use getRawVal() instead.
67
		// Values here are either one of a finite number of internal IDs,
68
		// or previously-stored user input (e.g. titles, user names) that were passed
69
		// to this endpoint by ResourceLoader itself from the canonical value.
70
		// Values do not come directly from user input and need not match.
71
72
		// List of modules
73
		$modules = $request->getRawVal( 'modules' );
74
		$this->modules = $modules ? self::expandModuleNames( $modules ) : [];
75
76
		// Various parameters
77
		$this->user = $request->getRawVal( 'user' );
78
		$this->debug = $request->getFuzzyBool(
79
			'debug',
80
			$resourceLoader->getConfig()->get( 'ResourceLoaderDebug' )
81
		);
82
		$this->only = $request->getRawVal( 'only', null );
83
		$this->version = $request->getRawVal( 'version', null );
84
		$this->raw = $request->getFuzzyBool( 'raw' );
85
86
		// Image requests
87
		$this->image = $request->getRawVal( 'image' );
88
		$this->variant = $request->getRawVal( 'variant' );
89
		$this->format = $request->getRawVal( 'format' );
90
91
		$this->skin = $request->getRawVal( 'skin' );
92
		$skinnames = Skin::getSkinNames();
93
		// If no skin is specified, or we don't recognize the skin, use the default skin
94
		if ( !$this->skin || !isset( $skinnames[$this->skin] ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->skin of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
95
			$this->skin = $resourceLoader->getConfig()->get( 'DefaultSkin' );
96
		}
97
	}
98
99
	/**
100
	 * Expand a string of the form jquery.foo,bar|jquery.ui.baz,quux to
101
	 * an array of module names like [ 'jquery.foo', 'jquery.bar',
102
	 * 'jquery.ui.baz', 'jquery.ui.quux' ]
103
	 * @param string $modules Packed module name list
104
	 * @return array Array of module names
105
	 */
106
	public static function expandModuleNames( $modules ) {
107
		$retval = [];
108
		$exploded = explode( '|', $modules );
109
		foreach ( $exploded as $group ) {
110
			if ( strpos( $group, ',' ) === false ) {
111
				// This is not a set of modules in foo.bar,baz notation
112
				// but a single module
113
				$retval[] = $group;
114
			} else {
115
				// This is a set of modules in foo.bar,baz notation
116
				$pos = strrpos( $group, '.' );
117
				if ( $pos === false ) {
118
					// Prefixless modules, i.e. without dots
119
					$retval = array_merge( $retval, explode( ',', $group ) );
120
				} else {
121
					// We have a prefix and a bunch of suffixes
122
					$prefix = substr( $group, 0, $pos ); // 'foo'
123
					$suffixes = explode( ',', substr( $group, $pos + 1 ) ); // [ 'bar', 'baz' ]
124
					foreach ( $suffixes as $suffix ) {
125
						$retval[] = "$prefix.$suffix";
126
					}
127
				}
128
			}
129
		}
130
		return $retval;
131
	}
132
133
	/**
134
	 * Return a dummy ResourceLoaderContext object suitable for passing into
135
	 * things that don't "really" need a context.
136
	 * @return ResourceLoaderContext
137
	 */
138
	public static function newDummyContext() {
139
		return new self( new ResourceLoader(
140
			ConfigFactory::getDefaultInstance()->makeConfig( 'main' ),
141
			LoggerFactory::getInstance( 'resourceloader' )
142
		), new FauxRequest( [] ) );
143
	}
144
145
	/**
146
	 * @return ResourceLoader
147
	 */
148
	public function getResourceLoader() {
149
		return $this->resourceLoader;
150
	}
151
152
	/**
153
	 * @return WebRequest
154
	 */
155
	public function getRequest() {
156
		return $this->request;
157
	}
158
159
	/**
160
	 * @since 1.27
161
	 * @return \Psr\Log\LoggerInterface
162
	 */
163
	public function getLogger() {
164
		return $this->logger;
165
	}
166
167
	/**
168
	 * @return array
169
	 */
170
	public function getModules() {
171
		return $this->modules;
172
	}
173
174
	/**
175
	 * @return string
176
	 */
177
	public function getLanguage() {
178
		if ( $this->language === null ) {
179
			// Must be a valid language code after this point (T64849)
180
			// Only support uselang values that follow built-in conventions (T102058)
181
			$lang = $this->getRequest()->getRawVal( 'lang', '' );
182
			// Stricter version of RequestContext::sanitizeLangCode()
183
			if ( !Language::isValidBuiltInCode( $lang ) ) {
184
				wfDebug( "Invalid user language code\n" );
185
				$lang = $this->getResourceLoader()->getConfig()->get( 'LanguageCode' );
186
			}
187
			$this->language = $lang;
188
		}
189
		return $this->language;
190
	}
191
192
	/**
193
	 * @return string
194
	 */
195 View Code Duplication
	public function getDirection() {
196
		if ( $this->direction === null ) {
197
			$this->direction = $this->getRequest()->getRawVal( 'dir' );
198
			if ( !$this->direction ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->direction of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
199
				// Determine directionality based on user language (bug 6100)
200
				$this->direction = Language::factory( $this->getLanguage() )->getDir();
201
			}
202
		}
203
		return $this->direction;
204
	}
205
206
	/**
207
	 * @return string
208
	 */
209
	public function getSkin() {
210
		return $this->skin;
211
	}
212
213
	/**
214
	 * @return string|null
215
	 */
216
	public function getUser() {
217
		return $this->user;
218
	}
219
220
	/**
221
	 * Get a Message object with context set.  See wfMessage for parameters.
222
	 *
223
	 * @since 1.27
224
	 * @param mixed ...
225
	 * @return Message
226
	 */
227
	public function msg() {
228
		return call_user_func_array( 'wfMessage', func_get_args() )
229
			->inLanguage( $this->getLanguage() )
230
			// Use a dummy title because there is no real title
231
			// for this endpoint, and the cache won't vary on it
232
			// anyways.
233
			->title( Title::newFromText( 'Dwimmerlaik' ) );
234
	}
235
236
	/**
237
	 * Get the possibly-cached User object for the specified username
238
	 *
239
	 * @since 1.25
240
	 * @return User
241
	 */
242
	public function getUserObj() {
243
		if ( $this->userObj === null ) {
244
			$username = $this->getUser();
245
			if ( $username ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $username of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
246
				// Use provided username if valid, fallback to anonymous user
247
				$this->userObj = User::newFromName( $username ) ?: new User;
248
			} else {
249
				// Anonymous user
250
				$this->userObj = new User;
251
			}
252
		}
253
254
		return $this->userObj;
255
	}
256
257
	/**
258
	 * @return bool
259
	 */
260
	public function getDebug() {
261
		return $this->debug;
262
	}
263
264
	/**
265
	 * @return string|null
266
	 */
267
	public function getOnly() {
268
		return $this->only;
269
	}
270
271
	/**
272
	 * @see ResourceLoaderModule::getVersionHash
273
	 * @see ResourceLoaderClientHtml::makeLoad
274
	 * @return string|null
275
	 */
276
	public function getVersion() {
277
		return $this->version;
278
	}
279
280
	/**
281
	 * @return bool
282
	 */
283
	public function getRaw() {
284
		return $this->raw;
285
	}
286
287
	/**
288
	 * @return string|null
289
	 */
290
	public function getImage() {
291
		return $this->image;
292
	}
293
294
	/**
295
	 * @return string|null
296
	 */
297
	public function getVariant() {
298
		return $this->variant;
299
	}
300
301
	/**
302
	 * @return string|null
303
	 */
304
	public function getFormat() {
305
		return $this->format;
306
	}
307
308
	/**
309
	 * If this is a request for an image, get the ResourceLoaderImage object.
310
	 *
311
	 * @since 1.25
312
	 * @return ResourceLoaderImage|bool false if a valid object cannot be created
313
	 */
314
	public function getImageObj() {
315
		if ( $this->imageObj === null ) {
316
			$this->imageObj = false;
317
318
			if ( !$this->image ) {
319
				return $this->imageObj;
320
			}
321
322
			$modules = $this->getModules();
323
			if ( count( $modules ) !== 1 ) {
324
				return $this->imageObj;
325
			}
326
327
			$module = $this->getResourceLoader()->getModule( $modules[0] );
328
			if ( !$module || !$module instanceof ResourceLoaderImageModule ) {
329
				return $this->imageObj;
330
			}
331
332
			$image = $module->getImage( $this->image, $this );
333
			if ( !$image ) {
334
				return $this->imageObj;
335
			}
336
337
			$this->imageObj = $image;
338
		}
339
340
		return $this->imageObj;
341
	}
342
343
	/**
344
	 * @return bool
345
	 */
346
	public function shouldIncludeScripts() {
347
		return $this->getOnly() === null || $this->getOnly() === 'scripts';
348
	}
349
350
	/**
351
	 * @return bool
352
	 */
353
	public function shouldIncludeStyles() {
354
		return $this->getOnly() === null || $this->getOnly() === 'styles';
355
	}
356
357
	/**
358
	 * @return bool
359
	 */
360
	public function shouldIncludeMessages() {
361
		return $this->getOnly() === null;
362
	}
363
364
	/**
365
	 * All factors that uniquely identify this request, except 'modules'.
366
	 *
367
	 * The list of modules is excluded here for legacy reasons as most callers already
368
	 * split up handling of individual modules. Including it here would massively fragment
369
	 * the cache and decrease its usefulness.
370
	 *
371
	 * E.g. Used by RequestFileCache to form a cache key for storing the reponse output.
372
	 *
373
	 * @return string
374
	 */
375
	public function getHash() {
376
		if ( !isset( $this->hash ) ) {
377
			$this->hash = implode( '|', [
378
				// Module content vary
379
				$this->getLanguage(),
380
				$this->getSkin(),
381
				$this->getDebug(),
382
				$this->getUser(),
383
				// Request vary
384
				$this->getOnly(),
385
				$this->getVersion(),
386
				$this->getRaw(),
387
				$this->getImage(),
388
				$this->getVariant(),
389
				$this->getFormat(),
390
			] );
391
		}
392
		return $this->hash;
393
	}
394
}
395