Completed
Push — master ( 6aee84...9d465f )
by mw
127:50 queued 92:51
created

DataTypeRegistry::getDefaultDataItemByType()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 8
ccs 1
cts 1
cp 1
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace SMW;
4
5
use SMWDataItem as DataItem;
6
use SMW\ExtraneousLanguage\ExtraneousLanguage;
7
use SMW\DataValues\TypeList;
8
9
/**
10
 * DataTypes registry class
11
 *
12
 * Registry class that manages datatypes, and provides various methods to access
13
 * the information
14
 *
15
 * @license GNU GPL v2+
16
 * @since 1.9
17
 *
18
 * @author Markus Krötzsch
19
 * @author Jeroen De Dauw
20
 * @author mwjames
21
 */
22
class DataTypeRegistry {
23
24
	/**
25
	 * @var DataTypeRegistry
26
	 */
27
	protected static $instance = null;
28
29
	/**
30
	 * @var ExtraneousLanguage
31
	 */
32
	private $extraneousLanguage;
33
34
	/**
35
	 * Array of type labels indexed by type ids. Used for datatype resolution.
36
	 *
37
	 * @var string[]
38
	 */
39
	private $typeLabels = array();
40
41
	/**
42
	 * Array of ids indexed by type aliases. Used for datatype resolution.
43
	 *
44
	 * @var string[]
45
	 */
46
	private $typeAliases = array();
47
48
	/**
49
	 * @var string[]
50
	 */
51
	private $canonicalLabels = array();
52
53
	/**
54
	 * Array of class names for creating new SMWDataValue, indexed by type
55
	 * id.
56
	 *
57
	 * @var string[]
58
	 */
59
	private $typeClasses;
60
61
	/**
62
	 * Array of data item classes, indexed by type id.
63
	 *
64
	 * @var integer[]
65
	 */
66
	private $typeDataItemIds;
67
68
	/**
69
	 * @var string[]
70
	 */
71
	private $subDataTypes = array();
72
73
	/**
74
	 * Lookup map that allows finding a datatype id given a label or alias.
75
	 * All labels and aliases (ie array keys) are stored lower case.
76
	 *
77
	 * @var string[]
78
	 */
79
	private $typeByLabelOrAliasLookup = array();
80
81
	/**
82
	 * Array of default types to use for making datavalues for dataitems.
83
	 *
84
	 * @var string[]
85
	 */
86
	private $defaultDataItemTypeMap = array(
87
		DataItem::TYPE_BLOB => '_txt', // Text type
88
		DataItem::TYPE_URI => '_uri', // URL/URI type
89
		DataItem::TYPE_WIKIPAGE => '_wpg', // Page type
90
		DataItem::TYPE_NUMBER => '_num', // Number type
91
		DataItem::TYPE_TIME => '_dat', // Time type
92
		DataItem::TYPE_BOOLEAN => '_boo', // Boolean type
93
		DataItem::TYPE_CONTAINER => '_rec', // Value list type (replacing former nary properties)
94
		DataItem::TYPE_GEO => '_geo', // Geographical coordinates
95
		DataItem::TYPE_CONCEPT => '__con', // Special concept page type
96
		DataItem::TYPE_PROPERTY => '__pro', // Property type
97
98
		// If either of the following two occurs, we want to see a PHP error:
99
		//DataItem::TYPE_NOTYPE => '',
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
100
		//DataItem::TYPE_ERROR => '',
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
101
	);
102
103
104
	/**
105
	 * @var Closure[]
106
	 */
107
	private $extraneousFunctions = array();
108
109
	/**
110
	 * @var Options
111
	 */
112
	private $options = null;
113
114
	/**
115
	 * Returns a DataTypeRegistry instance
116
	 *
117
	 * @since 1.9
118
	 *
119 345
	 * @return DataTypeRegistry
120
	 */
121 345
	public static function getInstance() {
122 292
123
		if ( self::$instance !== null ) {
124
			return self::$instance;
125 316
		}
126
127 316
		$extraneousLanguage = Localizer::getInstance()->getExtraneousLanguage();
128
129
		self::$instance = new self(
130
			$extraneousLanguage
131 316
		);
132
133 316
		self::$instance->initDatatypes( TypeList::getList() );
134 316
135 316
		self::$instance->setOption(
136
			'smwgDVFeatures',
137
			ApplicationFactory::getInstance()->getSettings()->get( 'smwgDVFeatures' )
138 316
		);
139
140
		return self::$instance;
141
	}
142
143
	/**
144
	 * Resets the DataTypeRegistry instance
145
	 *
146 331
	 * @since 1.9
147 331
	 */
148 331
	public static function clear() {
149 331
		self::$instance = null;
150 331
	}
151
152
	/**
153
	 * @since 1.9.0.2
154
	 *
155
	 * @param ExtraneousLanguage $extraneousLanguage
156
	 */
157 316
	public function __construct( ExtraneousLanguage $extraneousLanguage ) {
158 316
		$this->extraneousLanguage = $extraneousLanguage;
159 316
		$this->registerLabels();
160 316
	}
161
162 316
	/**
163 316
	 * @deprecated since 2.5, use DataTypeRegistry::getDataItemByType
164
	 */
165
	public function getDataItemId( $typeId ) {
166 316
		return $this->getDataItemByType( $typeId );
167 316
	}
168
169
	/**
170 316
	 * Get the preferred data item ID for a given type. The ID defines the
171 316
	 * appropriate data item class for processing data of this type. See
172
	 * DataItem for possible values.
173 316
	 *
174
	 * @note SMWDIContainer is a pseudo dataitem type that is used only in
175
	 * data input methods, but not for storing data. Types that work with
176
	 * SMWDIContainer use SMWDIWikiPage as their DI type. (Since SMW 1.8)
177
	 *
178
	 * @param $typeId string id string for the given type
179
	 * @return integer data item ID
180
	 */
181
	public function getDataItemByType( $typeId ) {
182
183
		if ( isset( $this->typeDataItemIds[$typeId] ) ) {
184
			return $this->typeDataItemIds[$typeId];
185
		}
186
187 284
		return DataItem::TYPE_NOTYPE;
188 284
	}
189 284
190
	/**
191
	 * @since  2.0
192 2
	 *
193
	 * @param string
194
	 *
195
	 * @return boolean
196
	 */
197
	public function isKnownByType( $typeId ) {
198
		return isset( $this->typeDataItemIds[$typeId] );
199
	}
200
201 16
	/**
202 16
	 * @since 2.4
203
	 *
204
	 * @param string $typeId
205
	 *
206
	 * @return boolean
207
	 */
208
	public function isSubDataType( $typeId ) {
209
		return isset( $this->subDataTypes[$typeId] ) && $this->subDataTypes[$typeId];
210
	}
211 228
212 228
	/**
213
	 * @since 2.5
214
	 *
215
	 * @param string $srcType
216
	 * @param string $tagType
217
	 *
218
	 * @return boolean
219
	 */
220
	public function isEqualByType( $srcType, $tagType ) {
221
		return $this->getDataItemByType( $srcType ) === $this->getDataItemByType( $tagType );
222
	}
223
224
	/**
225 2
	 * A function for registering/overwriting datatypes for SMW. Should be
226 2
	 * called from within the hook 'smwInitDatatypes'.
227 2
	 *
228 2
	 * @param $id string type ID for which this datatype is registered
229
	 * @param $className string name of the according subclass of SMWDataValue
230 2
	 * @param $dataItemId integer ID of the data item class that this data value uses, see DataItem
231 2
	 * @param $label mixed string label or false for types that cannot be accessed by users
232
	 * @param boolean $isSubDataType
233 2
	 */
234
	public function registerDataType( $id, $className, $dataItemId, $label = false, $isSubDataType = false ) {
235 316
		$this->typeClasses[$id] = $className;
236 316
		$this->typeDataItemIds[$id] = $dataItemId;
237 316
		$this->subDataTypes[$id] = $isSubDataType;
238 316
239
		if ( $label !== false ) {
240 316
			$this->registerTypeLabel( $id, $label );
241 316
		}
242 316
	}
243
244
	private function registerTypeLabel( $typeId, $typeLabel ) {
245
		$this->typeLabels[$typeId] = $typeLabel;
246
		$this->addTextToIdLookupMap( $typeId, $typeLabel );
247
	}
248
249
	private function addTextToIdLookupMap( $dataTypeId, $text ) {
250
		$this->typeByLabelOrAliasLookup[mb_strtolower($text)] = $dataTypeId;
251
	}
252
253 316
	/**
254 316
	 * Add a new alias label to an existing datatype id. Note that every ID
255 316
	 * should have a primary label, either provided by SMW or registered with
256 316
	 * registerDataType(). This function should be called from within the hook
257
	 * 'smwInitDatatypes'.
258
	 *
259
	 * @param string $typeId
260
	 * @param string $typeAlias
261
	 */
262
	public function registerDataTypeAlias( $typeId, $typeAlias ) {
263
		$this->typeAliases[$typeAlias] = $typeId;
264
		$this->addTextToIdLookupMap( $typeId, $typeAlias );
265
	}
266
267
	/**
268
	 * Look up the ID that identifies the datatype of the given label
269 10
	 * internally. This id is used for all internal operations. If the
270
	 * label does not belong to a known type, the empty string is returned.
271 10
	 *
272
	 * The lookup is case insensitive.
273 10
	 *
274 10
	 * @param string $label
275
	 *
276
	 * @return string
277 1
	 */
278
	public function findTypeId( $label ) {
279
280
		$label = mb_strtolower( $label );
281
282
		if ( isset( $this->typeByLabelOrAliasLookup[$label] ) ) {
283
			return $this->typeByLabelOrAliasLookup[$label];
284
		}
285
286
		return '';
287
	}
288 167
289
	/**
290 167
	 * @since 2.5
291
	 *
292 167
	 * @param string $label
293
	 * @param string|false $languageCode
294
	 *
295
	 * @return string
296 167
	 */
297
	public function findTypeByLanguage( $label, $languageCode = false ) {
298 167
299 167
		if ( !$languageCode ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $languageCode of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false 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...
300 167
			return $this->findTypeId( $label );
301
		}
302 167
303 167
		$extraneousLanguage = $this->extraneousLanguage->fetchByLanguageCode(
304 167
			$languageCode
305
		);
306
307
		return $extraneousLanguage->findDatatypeByLabel( $label );
308
	}
309
310
	/**
311
	 * Get the translated user label for a given internal ID. If the ID does
312
	 * not have a label associated with it in the current language, the
313
	 * empty string is returned. This is the case both for internal type ids
314
	 * and for invalid (unknown) type ids, so this method cannot be used to
315
	 * distinguish the two.
316
	 *
317
	 * @param string $id
318
	 *
319
	 * @return string
320
	 */
321
	public function findTypeLabel( $id ) {
322 176
323
		if ( isset( $this->typeLabels[$id] ) ) {
324 176
			return $this->typeLabels[$id];
325 176
		}
326
327
		// internal type without translation to user space;
328
		// might also happen for historic types after an upgrade --
329
		// alas, we have no idea what the former label would have been
330
		return '';
331 3
	}
332
333
	/**
334
	 * Returns a label for a typeId that is independent from the user/content
335
	 * language
336
	 *
337
	 * @since 2.3
338
	 *
339
	 * @return string
340
	 */
341
	public function findCanonicalLabelById( $id ) {
342 1
343
		if ( isset( $this->canonicalLabels[$id] ) ) {
344 1
			return $this->canonicalLabels[$id];
345 1
		}
346
347
		return '';
348
	}
349
350
	/**
351
	 * @since 2.4
352
	 *
353
	 * @return array
354
	 */
355
	public function getCanonicalDatatypeLabels() {
356
		return $this->canonicalLabels;
357
	}
358
359
	/**
360
	 * Return an array of all labels that a user might specify as the type of
361
	 * a property, and that are internal (i.e. not user defined). No labels are
362
	 * returned for internal types without user labels (e.g. the special types
363
	 * for some special properties), and for user defined types.
364
	 *
365
	 * @return array
366
	 */
367
	public function getKnownTypeLabels() {
368 282
		return $this->typeLabels;
369 282
	}
370
371
	/**
372
	 * @since 2.1
373
	 *
374
	 * @return array
375
	 */
376
	public function getKnownTypeAliases() {
377 282
		return $this->typeAliases;
378 282
	}
379
380
	/**
381
	 * @deprecated since 2.5, use DataTypeRegistry::getDefaultDataItemByType
382
	 */
383
	public function getDefaultDataItemTypeId( $diType ) {
0 ignored issues
show
Unused Code introduced by
The parameter $diType is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
384
		return $this->getDefaultDataItemByType( $typeId );
0 ignored issues
show
Bug introduced by
The variable $typeId does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
385
	}
386
387
	/**
388
	 * Returns a default DataItem for a matchable type ID
389
	 *
390 160
	 * @since 2.5
391
	 *
392 160
	 * @param string $diType
0 ignored issues
show
Bug introduced by
There is no parameter named $diType. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
393 159
	 *
394
	 * @return string|null
395
	 */
396 1
	public function getDefaultDataItemByType( $typeId ) {
397
398
		if ( isset( $this->defaultDataItemTypeMap[$typeId] ) ) {
399
			return $this->defaultDataItemTypeMap[$typeId];
400
		}
401
402
		return null;
403
	}
404
405
	/**
406
	 * Returns a class based on a typeId
407
	 *
408 254
	 * @since 1.9
409
	 *
410 254
	 * @param string $typeId
411 254
	 *
412
	 * @return string|null
413
	 */
414 1
	public function getDataTypeClassById( $typeId ) {
415
416
		if ( $this->hasDataTypeClassById( $typeId ) ) {
417
			return $this->typeClasses[$typeId];
418
		}
419
420
		return null;
421
	}
422
423
	/**
424
	 * Whether a datatype class is registered for a particular typeId
425
	 *
426 254
	 * @since 1.9
427 254
	 *
428
	 * @param string $typeId
429
	 *
430
	 * @return boolean
431
	 */
432
	public function hasDataTypeClassById( $typeId ) {
433
		return isset( $this->typeClasses[$typeId] ) && class_exists( $this->typeClasses[$typeId] );
434
	}
435 316
436
	/**
437
	 * Gather all available datatypes and label<=>id<=>datatype
438
	 * associations. This method is called before most methods of this
439
	 * factory.
440 316
	 */
441
	protected function initDatatypes( array $typeList ) {
442
443
		foreach ( $typeList as $id => $definition ) {
444
445
			if ( isset( $definition[0] ) ) {
446
				$this->typeClasses[$id] = $definition[0];
447
			}
448
449
			$this->typeDataItemIds[$id] = $definition[1];
450
			$this->subDataTypes[$id] = $definition[2];
451
		}
452
453
		// Deprecated since 1.9
454
		\Hooks::run( 'smwInitDatatypes' );
455
456
		// Since 1.9
457
		\Hooks::run( 'SMW::DataType::initTypes', array( $this ) );
458
	}
459
460
	/**
461
	 * Inject services and objects that are planned to be used during the invocation of
462
	 * a DataValue
463
	 *
464
	 * @since 2.3
465
	 *
466
	 * @param string  $name
467
	 * @param \Closure $callback
468
	 */
469
	public function registerExtraneousFunction( $name, \Closure $callback ) {
470
		$this->extraneousFunctions[$name] = $callback;
471
	}
472
473
	/**
474
	 * @since 2.3
475
	 *
476
	 * @return Closure[]
477
	 */
478
	public function getExtraneousFunctions() {
479
		return $this->extraneousFunctions;
480
	}
481
482
	/**
483
	 * @since 2.4
484
	 *
485
	 * @return Options
486 316
	 */
487 316
	public function getOptions() {
488 316
489 316
		if ( $this->options === null ) {
490 316
			$this->options = new Options();
491 316
		}
492 316
493 316
		return $this->options;
494 316
	}
495 316
496 316
	/**
497 316
	 * @since 2.4
498 316
	 *
499 316
	 * @param string $key
500 316
	 * @param string $value
501 316
	 */
502 316
	public function setOption( $key, $value ) {
503 316
		$this->getOptions()->set( $key, $value );
504 316
	}
505 316
506 316
	private function registerLabels() {
507 316
508 316
		foreach ( $this->extraneousLanguage->getDatatypeLabels() as $typeId => $typeLabel ) {
509
			$this->registerTypeLabel( $typeId, $typeLabel );
510 316
		}
511 316
512 316
		foreach ( $this->extraneousLanguage->getDatatypeAliases() as $typeAlias => $typeId ) {
513 316
			$this->registerDataTypeAlias( $typeId, $typeAlias );
514 316
		}
515 316
516 316
		foreach ( $this->extraneousLanguage->getCanonicalDatatypeLabels() as $label => $id ) {
517 316
			$this->canonicalLabels[$id] = $label;
518 316
		}
519 316
	}
520 316
521
}
522