Completed
Push — master ( 3837d1...eff53c )
by mw
164:04 queued 128:48
created

src/DataTypeRegistry.php (1 issue)

super-globals are not used.

Coding Style Minor

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace SMW;
4
5
use SMW\DataValues\ValueFormatterRegistry;
6
use SMW\DataValues\ValueFormatters\DataValueFormatter;
7
use SMW\Deserializers\DVDescriptionDeserializer\DescriptionDeserializer;
8
use SMW\Deserializers\DVDescriptionDeserializerRegistry;
9
use SMWDataItem as DataItem;
10
11
/**
12
 * DataTypes registry class
13
 *
14
 * Registry class that manages datatypes, and provides various methods to access
15
 * the information
16
 *
17
 * @license GNU GPL v2+
18
 * @since 1.9
19
 *
20
 * @author Markus Krötzsch
21
 * @author Jeroen De Dauw
22
 * @author mwjames
23
 */
24
class DataTypeRegistry {
25
26
	/**
27
	 * @var DataTypeRegistry
28
	 */
29
	protected static $instance = null;
30
31
	/**
32
	 * Array of type labels indexed by type ids. Used for datatype resolution.
33
	 *
34
	 * @var string[]
35
	 */
36
	private $typeLabels = array();
37
38
	/**
39
	 * Array of ids indexed by type aliases. Used for datatype resolution.
40
	 *
41
	 * @var string[]
42
	 */
43
	private $typeAliases = array();
44
45
	/**
46
	 * @var string[]
47
	 */
48
	private $canonicalLabels = array();
49
50
	/**
51
	 * Array of class names for creating new SMWDataValue, indexed by type
52
	 * id.
53
	 *
54
	 * @var string[]
55
	 */
56
	private $typeClasses;
57
58
	/**
59
	 * Array of data item classes, indexed by type id.
60
	 *
61
	 * @var integer[]
62
	 */
63
	private $typeDataItemIds;
64
65
	/**
66
	 * @var string[]
67
	 */
68
	private $subDataTypes = array();
69
70
	/**
71
	 * Lookup map that allows finding a datatype id given a label or alias.
72
	 * All labels and aliases (ie array keys) are stored lower case.
73
	 *
74
	 * @var string[]
75
	 */
76
	private $typeByLabelOrAliasLookup = array();
77
78
	/**
79
	 * Array of default types to use for making datavalues for dataitems.
80
	 *
81
	 * @var string[]
82
	 */
83
	private $defaultDataItemTypeIds = array(
84
		DataItem::TYPE_BLOB => '_txt', // Text type
85
		DataItem::TYPE_URI => '_uri', // URL/URI type
86
		DataItem::TYPE_WIKIPAGE => '_wpg', // Page type
87
		DataItem::TYPE_NUMBER => '_num', // Number type
88
		DataItem::TYPE_TIME => '_dat', // Time type
89
		DataItem::TYPE_BOOLEAN => '_boo', // Boolean type
90
		DataItem::TYPE_CONTAINER => '_rec', // Value list type (replacing former nary properties)
91
		DataItem::TYPE_GEO => '_geo', // Geographical coordinates
92
		DataItem::TYPE_CONCEPT => '__con', // Special concept page type
93
		DataItem::TYPE_PROPERTY => '__pro', // Property type
94
95
		// If either of the following two occurs, we want to see a PHP error:
96
		//DataItem::TYPE_NOTYPE => '',
97
		//DataItem::TYPE_ERROR => '',
98
	);
99
100
101
	/**
102
	 * @var Closure[]
103
	 */
104
	private $extraneousFunctions = array();
105
106
	/**
107
	 * @var Options
108
	 */
109
	private $options = null;
110
111
	/**
112
	 * Returns a DataTypeRegistry instance
113
	 *
114
	 * @since 1.9
115
	 *
116
	 * @return DataTypeRegistry
117
	 */
118 319
	public static function getInstance() {
0 ignored issues
show
getInstance uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
119
120 319
		if ( self::$instance === null ) {
121
122 295
			self::$instance = new self(
123 295
				$GLOBALS['smwgContLang']->getDatatypeLabels(),
124 295
				$GLOBALS['smwgContLang']->getDatatypeAliases(),
125 295
				$GLOBALS['smwgContLang']->getCanonicalDatatypeLabels()
126
			);
127
128 295
			self::$instance->initDatatypes();
129
130 295
			self::$instance->setOption(
131 295
				'smwgDVFeatures',
132 295
				ApplicationFactory::getInstance()->getSettings()->get( 'smwgDVFeatures' )
133
			);
134
		}
135
136 319
		return self::$instance;
137
	}
138
139
	/**
140
	 * Resets the DataTypeRegistry instance
141
	 *
142
	 * @since 1.9
143
	 */
144 310
	public static function clear() {
145 310
		self::$instance = null;
146 310
		ValueFormatterRegistry::getInstance()->clear();
147 310
		DVDescriptionDeserializerRegistry::getInstance()->clear();
148 310
	}
149
150
	/**
151
	 * @since 1.9.0.2
152
	 *
153
	 * @param array $typeLabels
154
	 * @param array $typeAliases
155
	 */
156 295
	public function __construct( array $typeLabels = array() , array $typeAliases = array(), array $canonicalLabels = array() ) {
157 295
		foreach ( $typeLabels as $typeId => $typeLabel ) {
158 295
			$this->registerTypeLabel( $typeId, $typeLabel );
159
		}
160
161 295
		foreach ( $typeAliases as $typeAlias => $typeId ) {
162 295
			$this->registerDataTypeAlias( $typeId, $typeAlias );
163
		}
164
165 295
		foreach ( $canonicalLabels as $label => $id ) {
166 295
			$this->canonicalLabels[$id] = $label;
167
		}
168 295
	}
169
170
	/**
171
	 * Get the preferred data item ID for a given type. The ID defines the
172
	 * appropriate data item class for processing data of this type. See
173
	 * DataItem for possible values.
174
	 *
175
	 * @note SMWDIContainer is a pseudo dataitem type that is used only in
176
	 * data input methods, but not for storing data. Types that work with
177
	 * SMWDIContainer use SMWDIWikiPage as their DI type. (Since SMW 1.8)
178
	 *
179
	 * @param $typeId string id string for the given type
180
	 * @return integer data item ID
181
	 */
182 257
	public function getDataItemId( $typeId ) {
183 257
		if ( isset( $this->typeDataItemIds[$typeId] ) ) {
184 257
			return $this->typeDataItemIds[$typeId];
185
		}
186
187 2
		return DataItem::TYPE_NOTYPE;
188
	}
189
190
	/**
191
	 * @since  2.0
192
	 *
193
	 * @param string
194
	 * @return boolean
195
	 */
196 16
	public function isKnownTypeId( $typeId ) {
197 16
		return isset( $this->typeDataItemIds[$typeId] );
198
	}
199
200
	/**
201
	 * @since 2.4
202
	 *
203
	 * @param string
204
	 * @return boolean
205
	 */
206 207
	public function isSubDataType( $typeId ) {
207 207
		return isset( $this->subDataTypes[$typeId] ) && $this->subDataTypes[$typeId];
208
	}
209
210
	/**
211
	 * A function for registering/overwriting datatypes for SMW. Should be
212
	 * called from within the hook 'smwInitDatatypes'.
213
	 *
214
	 * @param $id string type ID for which this datatype is registered
215
	 * @param $className string name of the according subclass of SMWDataValue
216
	 * @param $dataItemId integer ID of the data item class that this data value uses, see DataItem
217
	 * @param $label mixed string label or false for types that cannot be accessed by users
218
	 * @param boolean $isSubDataType
219
	 */
220 2
	public function registerDataType( $id, $className, $dataItemId, $label = false, $isSubDataType = false ) {
221 2
		$this->typeClasses[$id] = $className;
222 2
		$this->typeDataItemIds[$id] = $dataItemId;
223 2
		$this->subDataTypes[$id] = $isSubDataType;
224
225 2
		if ( $label !== false ) {
226 2
			$this->registerTypeLabel( $id, $label );
227
		}
228 2
	}
229
230 295
	private function registerTypeLabel( $typeId, $typeLabel ) {
231 295
		$this->typeLabels[$typeId] = $typeLabel;
232 295
		$this->addTextToIdLookupMap( $typeId, $typeLabel );
233 295
	}
234
235 295
	private function addTextToIdLookupMap( $dataTypeId, $text ) {
236 295
		$this->typeByLabelOrAliasLookup[strtolower($text)] = $dataTypeId;
237 295
	}
238
239
	/**
240
	 * Add a new alias label to an existing datatype id. Note that every ID
241
	 * should have a primary label, either provided by SMW or registered with
242
	 * registerDataType(). This function should be called from within the hook
243
	 * 'smwInitDatatypes'.
244
	 *
245
	 * @param string $typeId
246
	 * @param string $typeAlias
247
	 */
248 295
	public function registerDataTypeAlias( $typeId, $typeAlias ) {
249 295
		$this->typeAliases[$typeAlias] = $typeId;
250 295
		$this->addTextToIdLookupMap( $typeId, $typeAlias );
251 295
	}
252
253
	/**
254
	 * Look up the ID that identifies the datatype of the given label
255
	 * internally. This id is used for all internal operations. If the
256
	 * label does not belong to a known type, the empty string is returned.
257
	 *
258
	 * The lookup is case insensitive.
259
	 *
260
	 * @param string $label
261
	 *
262
	 * @return string
263
	 */
264 9
	public function findTypeId( $label ) {
265
266 9
		$label = strtolower( $label );
267
268 9
		if ( isset( $this->typeByLabelOrAliasLookup[$label] ) ) {
269 9
			return $this->typeByLabelOrAliasLookup[$label];
270
		}
271
272 1
		return '';
273
	}
274
275
	/**
276
	 * @since 2.5
277
	 *
278
	 * @param string $label
279
	 * @param string|false $languageCode
280
	 *
281
	 * @return string
282
	 */
283 148
	public function findTypeIdByLanguage( $label, $languageCode = false ) {
284
285 148
		$label = mb_strtolower( $label );
286
287 148
		if ( !$languageCode ) {
288
			return $this->findTypeId( $label );
289
		}
290
291 148
		$extraneousLanguage = Localizer::getInstance()->getExtraneousLanguage( $languageCode );
292
293 148
		$datatypeLabels = $extraneousLanguage->getDatatypeLabels();
294 148
		$datatypeLabels = array_flip( $datatypeLabels );
295 148
		$datatypeLabels += $extraneousLanguage->getDatatypeAliases();
296
297 148
		foreach ( $datatypeLabels as $key => $id ) {
298 148
			if ( mb_strtolower( $key ) === $label ) {
299 148
				return $id;
300
			}
301
		}
302
303
		return '';
304
	}
305
306
	/**
307
	 * Get the translated user label for a given internal ID. If the ID does
308
	 * not have a label associated with it in the current language, the
309
	 * empty string is returned. This is the case both for internal type ids
310
	 * and for invalid (unknown) type ids, so this method cannot be used to
311
	 * distinguish the two.
312
	 *
313
	 * @param string $id
314
	 *
315
	 * @return string
316
	 */
317 156
	public function findTypeLabel( $id ) {
318
319 156
		if ( isset( $this->typeLabels[$id] ) ) {
320 156
			return $this->typeLabels[$id];
321
		}
322
323
		// internal type without translation to user space;
324
		// might also happen for historic types after an upgrade --
325
		// alas, we have no idea what the former label would have been
326 3
		return '';
327
	}
328
329
	/**
330
	 * Returns a label for a typeId that is independent from the user/content
331
	 * language
332
	 *
333
	 * @since 2.3
334
	 *
335
	 * @return string
336
	 */
337 1
	public function findCanonicalLabelById( $id ) {
338
339 1
		if ( isset( $this->canonicalLabels[$id] ) ) {
340 1
			return $this->canonicalLabels[$id];
341
		}
342
343
		return '';
344
	}
345
346
	/**
347
	 * @since 2.4
348
	 *
349
	 * @return array
350
	 */
351
	public function getCanonicalDatatypeLabels() {
352
		return $this->canonicalLabels;
353
	}
354
355
	/**
356
	 * Return an array of all labels that a user might specify as the type of
357
	 * a property, and that are internal (i.e. not user defined). No labels are
358
	 * returned for internal types without user labels (e.g. the special types
359
	 * for some special properties), and for user defined types.
360
	 *
361
	 * @return array
362
	 */
363 260
	public function getKnownTypeLabels() {
364 260
		return $this->typeLabels;
365
	}
366
367
	/**
368
	 * @since 2.1
369
	 *
370
	 * @return array
371
	 */
372 260
	public function getKnownTypeAliases() {
373 260
		return $this->typeAliases;
374
	}
375
376
	/**
377
	 * Returns a default DataItemId
378
	 *
379
	 * @since 1.9
380
	 *
381
	 * @param string $diType
382
	 *
383
	 * @return string|null
384
	 */
385 141
	public function getDefaultDataItemTypeId( $diType ) {
386
387 141
		if ( isset( $this->defaultDataItemTypeIds[$diType] ) ) {
388 140
			return $this->defaultDataItemTypeIds[$diType];
389
		}
390
391 1
		return null;
392
	}
393
394
	/**
395
	 * Returns a class based on a typeId
396
	 *
397
	 * @since 1.9
398
	 *
399
	 * @param string $typeId
400
	 *
401
	 * @return string|null
402
	 */
403 233
	public function getDataTypeClassById( $typeId ) {
404
405 233
		if ( $this->hasDataTypeClassById( $typeId ) ) {
406 233
			return $this->typeClasses[$typeId];
407
		}
408
409 1
		return null;
410
	}
411
412
	/**
413
	 * Whether a datatype class is registered for a particular typeId
414
	 *
415
	 * @since 1.9
416
	 *
417
	 * @param string $typeId
418
	 *
419
	 * @return boolean
420
	 */
421 233
	public function hasDataTypeClassById( $typeId ) {
422 233
		return isset( $this->typeClasses[$typeId] ) && class_exists( $this->typeClasses[$typeId] );
423
	}
424
425
	/**
426
	 * Gather all available datatypes and label<=>id<=>datatype
427
	 * associations. This method is called before most methods of this
428
	 * factory.
429
	 */
430 295
	protected function initDatatypes() {
431
		// Setup built-in datatypes.
432
		// NOTE: all ids must start with underscores, where two underscores indicate
433
		// truly internal (non user-acceptable types). All others should also get a
434
		// translation in the language files, or they won't be available for users.
435 295
		$this->typeClasses = array(
436
			'_txt'  => 'SMWStringValue', // Text type
437
			'_cod'  => 'SMWStringValue', // Code type
438
			'_str'  => 'SMWStringValue', // DEPRECATED Will vanish after SMW 1.9; use '_txt'
439
			'_ema'  => 'SMWURIValue', // Email type
440
			'_uri'  => 'SMWURIValue', // URL/URI type
441
			'_anu'  => 'SMWURIValue', // Annotation URI type
442
			'_tel'  => 'SMW\DataValues\TelephoneUriValue', // Phone number (URI) type
443
			'_wpg'  => 'SMWWikiPageValue', // Page type
444
			'_wpp'  => 'SMWWikiPageValue', // Property page type TODO: make available to user space
445
			'_wpc'  => 'SMWWikiPageValue', // Category page type TODO: make available to user space
446
			'_wpf'  => 'SMWWikiPageValue', // Form page type for Semantic Forms
447
			'_num'  => 'SMWNumberValue', // Number type
448
			'_tem'  => 'SMW\DataValues\TemperatureValue', // Temperature type
449
			'_dat'  => 'SMWTimeValue', // Time type
450
			'_boo'  => 'SMW\DataValues\BooleanValue', // Boolean type
451
			'_rec'  => 'SMWRecordValue', // Value list type (replacing former nary properties)
452
			'_mlt_rec'  => 'SMW\DataValues\MonolingualTextValue',
453
			'_ref_rec'  => 'SMW\DataValues\ReferenceValue',
454
			'_qty'  => 'SMWQuantityValue', // Type for numbers with units of measurement
455
			// Special types are not avaialble directly for users (and have no local language name):
456
			'__typ' => 'SMWTypesValue', // Special type page type
457
			'__pls' => 'SMWPropertyListValue', // Special type list for decalring _rec properties
458
			'__con' => 'SMWConceptValue', // Special concept page type
459
			'__sps' => 'SMWStringValue', // Special string type
460
			'__spu' => 'SMWURIValue', // Special uri type
461
			'__sob' => 'SMWWikiPageValue', // Special subobject type
462
			'__sup' => 'SMWWikiPageValue', // Special subproperty type
463
			'__suc' => 'SMWWikiPageValue', // Special subcategory type
464
			'__spf' => 'SMWWikiPageValue', // Special Form page type for Semantic Forms
465
			'__sin' => 'SMWWikiPageValue', // Special instance of type
466
			'__red' => 'SMWWikiPageValue', // Special redirect type
467
			'__err' => 'SMWErrorValue', // Special error type
468
			'__errt' => 'SMW\DataValues\ErrorMsgTextValue', // Special error type
469
			'__imp' => 'SMW\DataValues\ImportValue', // Special import vocabulary type
470
			'__pro' => 'SMWPropertyValue', // Property type (possibly predefined, no always based on a page)
471
			'__key' => 'SMWStringValue', // Sort key of a page
472
			'__lcode' => 'SMW\DataValues\LanguageCodeValue',
473
			'__pval' => 'SMW\DataValues\AllowsListValue',
474
			'__pvap' => 'SMW\DataValues\AllowsPatternValue',
475
			'__pvuc' => 'SMW\DataValues\UniquenessConstraintValue',
476
			'_eid' => 'SMW\DataValues\ExternalIdentifierValue',
477
			'__pefu' => 'SMW\DataValues\ExternalFormatterUriValue',
478
			'__pchn' => 'SMW\DataValues\PropertyChainValue',
479
		);
480
481 295
		$this->typeDataItemIds = array(
482 295
			'_txt'  => DataItem::TYPE_BLOB, // Text type
483 295
			'_cod'  => DataItem::TYPE_BLOB, // Code type
484 295
			'_str'  => DataItem::TYPE_BLOB, // DEPRECATED Will vanish after SMW 1.9; use '_txt'
485 295
			'_ema'  => DataItem::TYPE_URI, // Email type
486 295
			'_uri'  => DataItem::TYPE_URI, // URL/URI type
487 295
			'_anu'  => DataItem::TYPE_URI, // Annotation URI type
488 295
			'_tel'  => DataItem::TYPE_URI, // Phone number (URI) type
489 295
			'_wpg'  => DataItem::TYPE_WIKIPAGE, // Page type
490 295
			'_wpp'  => DataItem::TYPE_WIKIPAGE, // Property page type TODO: make available to user space
491 295
			'_wpc'  => DataItem::TYPE_WIKIPAGE, // Category page type TODO: make available to user space
492 295
			'_wpf'  => DataItem::TYPE_WIKIPAGE, // Form page type for Semantic Forms
493 295
			'_num'  => DataItem::TYPE_NUMBER, // Number type
494 295
			'_tem'  => DataItem::TYPE_NUMBER, // Temperature type
495 295
			'_dat'  => DataItem::TYPE_TIME, // Time type
496 295
			'_boo'  => DataItem::TYPE_BOOLEAN, // Boolean type
497 295
			'_rec'  => DataItem::TYPE_WIKIPAGE, // Value list type (replacing former nary properties)
498 295
			'_mlt_rec' => DataItem::TYPE_WIKIPAGE, // Monolingual text container
499 295
			'_ref_rec' => DataItem::TYPE_WIKIPAGE, // Reference container
500 295
			'_geo'  => DataItem::TYPE_GEO, // Geographical coordinates
501 295
			'_gpo'  => DataItem::TYPE_BLOB, // Geographical polygon
502 295
			'_qty'  => DataItem::TYPE_NUMBER, // Type for numbers with units of measurement
503 295
			'_eid' => DataItem::TYPE_BLOB, // External ID
504
			// Special types are not available directly for users (and have no local language name):
505 295
			'__typ' => DataItem::TYPE_URI, // Special type page type
506 295
			'__pls' => DataItem::TYPE_BLOB, // Special type list for decalring _rec properties
507 295
			'__con' => DataItem::TYPE_CONCEPT, // Special concept page type
508 295
			'__sps' => DataItem::TYPE_BLOB, // Special string type
509 295
			'__pval' => DataItem::TYPE_BLOB, // Special string type
510 295
			'__spu' => DataItem::TYPE_URI, // Special uri type
511 295
			'__sob' => DataItem::TYPE_WIKIPAGE, // Special subobject type
512 295
			'__sup' => DataItem::TYPE_WIKIPAGE, // Special subproperty type
513 295
			'__suc' => DataItem::TYPE_WIKIPAGE, // Special subcategory type
514 295
			'__spf' => DataItem::TYPE_WIKIPAGE, // Special Form page type for Semantic Forms
515 295
			'__sin' => DataItem::TYPE_WIKIPAGE, // Special instance of type
516 295
			'__red' => DataItem::TYPE_WIKIPAGE, // Special redirect type
517 295
			'__err' => DataItem::TYPE_ERROR, // Special error type
518 295
			'__errt' => DataItem::TYPE_BLOB, // error text
519 295
			'__imp' => DataItem::TYPE_BLOB, // Special import vocabulary type
520 295
			'__pro' => DataItem::TYPE_PROPERTY, // Property type (possibly predefined, no always based on a page)
521 295
			'__key' => DataItem::TYPE_BLOB, // Sort key of a page
522 295
			'__lcode' => DataItem::TYPE_BLOB, // Language code
523 295
			'__pvap' => DataItem::TYPE_BLOB, // Allows pattern
524 295
			'__pvuc' => DataItem::TYPE_BOOLEAN, // Uniqueness constraint
525 295
			'__pefu' => DataItem::TYPE_URI, // External formatter uri
526 295
			'__pchn' => DataItem::TYPE_BLOB, // Property chain
527
		);
528
529 295
		$this->subDataTypes = array(
530
			'__sob' => true,
531
			'_rec'  => true,
532
			'_mlt_rec' => true,
533
			'_ref_rec' => true
534
		);
535
536
		// Deprecated since 1.9
537 295
		\Hooks::run( 'smwInitDatatypes' );
538
539
		// Since 1.9
540 295
		\Hooks::run( 'SMW::DataType::initTypes', array( $this ) );
541 295
	}
542
543
	/**
544
	 * @since 2.4
545
	 *
546
	 * @param DataValueFormatter $dataValueFormatter
547
	 */
548 1
	public function registerDataValueFormatter( DataValueFormatter $dataValueFormatter ) {
549 1
		ValueFormatterRegistry::getInstance()->registerDataValueFormatter( $dataValueFormatter );
550 1
	}
551
552
	/**
553
	 * @since 2.4
554
	 *
555
	 * @param DescriptionDeserializer $descriptionDeserializer
556
	 */
557 1
	public function registerDVDescriptionDeserializer( DescriptionDeserializer $descriptionDeserializer ) {
558 1
		DVDescriptionDeserializerRegistry::getInstance()->registerDescriptionDeserializer( $descriptionDeserializer );
559 1
	}
560
561
	/**
562
	 * Inject services and objects that are planned to be used during the invocation of
563
	 * a DataValue
564
	 *
565
	 * @since 2.3
566
	 *
567
	 * @param string  $name
568
	 * @param \Closure $callback
569
	 */
570 1
	public function registerExtraneousFunction( $name, \Closure $callback ) {
571 1
		$this->extraneousFunctions[$name] = $callback;
572 1
	}
573
574
	/**
575
	 * @since 2.3
576
	 *
577
	 * @return Closure[]
578
	 */
579 233
	public function getExtraneousFunctions() {
580 233
		return $this->extraneousFunctions;
581
	}
582
583
	/**
584
	 * @since 2.4
585
	 *
586
	 * @return Options
587
	 */
588 310
	public function getOptions() {
589
590 310
		if ( $this->options === null ) {
591 295
			$this->options = new Options();
592
		}
593
594 310
		return $this->options;
595
	}
596
597
	/**
598
	 * @since 2.4
599
	 *
600
	 * @param string $key
601
	 * @param string $value
602
	 */
603 295
	public function setOption( $key, $value ) {
604 295
		$this->getOptions()->set( $key, $value );
605 295
	}
606
607
}
608