Completed
Push — master ( 14d2bd...06e609 )
by mw
81:37 queued 59:24
created

includes/dataitems/SMW_DI_Property.php (1 issue)

Checks method declaration static before visibility

Coding Style Informational

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 InvalidArgumentException;
6
use RuntimeException;
7
use SMWDataItem;
8
use SMWDIUri;
9
10
/**
11
 * This class implements Property data items.
12
 *
13
 * @note PropertyRegistry class manages global registrations of
14
 * predefined (built-in) properties, and maintains an association of
15
 * property IDs, localized labels, and aliases.
16
 *
17
 * @since 1.6
18
 *
19
 * @author Markus Krötzsch
20
 * @author Jeroen De Dauw
21
 * @author mwjames
22
 */
23
class DIProperty extends SMWDataItem {
24
25
	/**
26
	 * @see PropertyRegistry::registerPredefinedProperties
27
	 */
28
	const TYPE_SUBOBJECT  = '_SOBJ';
29
	const TYPE_ERROR      = '_ERRP';
30
	const TYPE_CATEGORY = '_INST';
31
	const TYPE_SUBCATEGORY = '_SUBC';
32
	const TYPE_SORTKEY = '_SKEY';
33
	const TYPE_MODIFICATION_DATE = '_MDAT';
34
	const TYPE_CREATION_DATE = '_CDAT';
35
	const TYPE_LAST_EDITOR = '_LEDT';
36
	const TYPE_NEW_PAGE = '_NEWP';
37
	const TYPE_HAS_TYPE = '_TYPE';
38
	const TYPE_CONVERSION = '_CONV';
39
	const TYPE_ASKQUERY = '_ASK';
40
	const TYPE_MEDIA = '_MEDIA';
41
	const TYPE_MIME = '_MIME';
42
	const TYPE_DISPLAYTITLE = '_DTITLE';
43
44
	/**
45
	 * Either an internal SMW property key (starting with "_") or the DB
46
	 * key of a property page in the wiki.
47
	 * @var string
48
	 */
49
	private $m_key;
50
51
	/**
52
	 * Whether to take the inverse of this property or not.
53
	 * @var boolean
54
	 */
55
	private $m_inverse;
56
57
	/**
58
	 * Cache for property type ID.
59
	 * @var string
60
	 */
61
	private $m_proptypeid;
62
63
	/**
64
	 * Interwiki prefix for when a property represents a non-local entity
65
	 *
66
	 * @var string
67
	 */
68
	private $interwiki = '';
69
70
	/**
71
	 * Initialise a property. This constructor checks that keys of
72
	 * predefined properties do really exist (in the current configuration
73
	 * of the wiki). No check is performed to see if a user label is in
74
	 * fact the label or alias of a predefined property. If this should be
75
	 * done, the function self::newFromUserLabel() can be used.
76
	 *
77
	 * @param $key string key for the property (internal SMW key or wikipage DB key)
78
	 * @param $inverse boolean states if the inverse of the property is constructed
79
	 */
80 269
	public function __construct( $key, $inverse = false ) {
81 269
		if ( ( $key === '' ) || ( $key{0} == '-' ) ) {
82
			throw new InvalidPropertyException( "Illegal property key \"$key\"." );
83
		}
84
85 269
		if ( $key{0} == '_' ) {
86 257
			if ( !PropertyRegistry::getInstance()->isKnownPropertyId( $key ) ) {
87 1
				throw new InvalidPredefinedPropertyException( "There is no predefined property with \"$key\"." );
88
			}
89
		}
90
91 268
		$this->m_key = $key;
92 268
		$this->m_inverse = $inverse;
93 268
	}
94
95
	/**
96
	 * @return integer
97
	 */
98 194
	public function getDIType() {
99 194
		return SMWDataItem::TYPE_PROPERTY;
100
	}
101
102
	/**
103
	 * @return string
104
	 */
105 257
	public function getKey() {
106 257
		return $this->m_key;
107
	}
108
109
	/**
110
	 * @return boolean
111
	 */
112 252
	public function isInverse() {
113 252
		return $this->m_inverse;
114
	}
115
116
	/**
117
	 * @return string
118
	 */
119 1
	public function getSortKey() {
120 1
		return $this->m_key;
121
	}
122
123
	/**
124
	 * Specifies whether values of this property should be shown in the
125
	 * Factbox. A property may wish to prevent this if either
126
	 * (1) its information is really dull, e.g. being a mere copy of
127
	 * information that is obvious from other things that are shown, or
128
	 * (2) the property is set in a hook after parsing, so that it is not
129
	 * reliably available when Factboxes are displayed. If a property is
130
	 * internal so it should never be observed by users, then it is better
131
	 * to just not associate any translated label with it, so it never
132
	 * appears anywhere.
133
	 *
134
	 * Examples of properties that are not shown include Modification date
135
	 * (not available in time), and Has improper value for (errors are
136
	 * shown directly on the page anyway).
137
	 *
138
	 * @return boolean
139
	 */
140 209
	public function isShown() {
141
142 209
		if ( $this->isUserDefined() ) {
143 1
			return true;
144
		}
145
146 209
		return PropertyRegistry::getInstance()->isVisibleToUser( $this->m_key );
147
	}
148
149
	/**
150
	 * Return true if this is a usual wiki property that is defined by a
151
	 * wiki page, and not a property that is pre-defined in the wiki.
152
	 *
153
	 * @return boolean
154
	 */
155 252
	public function isUserDefined() {
156 252
		return $this->m_key{0} != '_';
157
	}
158
159
	/**
160
	 * Whether a user can freely use this property for value declarations or
161
	 * not.
162
	 *
163
	 * @note A user defined property is generally assumed to be unrestricted
164
	 * for usage
165
	 *
166
	 * @since 2.2
167
	 *
168
	 * @return boolean
169
	 */
170 162
	public function isUnrestrictedForUse() {
171
172 162
		if ( $this->isUserDefined() ) {
173 155
			return true;
174
		}
175
176 128
		return PropertyRegistry::getInstance()->isUnrestrictedForAnnotationUse( $this->m_key );
177
	}
178
179
	/**
180
	 * Find a user-readable label for this property, or return '' if it is
181
	 * a predefined property that has no label. For inverse properties, the
182
	 * label starts with a "-".
183
	 *
184
	 * @return string
185
	 */
186 234
	public function getLabel() {
187 234
		$prefix = $this->m_inverse ? '-' : '';
188
189 234
		if ( $this->isUserDefined() ) {
190 187
			return $prefix . str_replace( '_', ' ', $this->m_key );
191
		}
192
193 232
		return $prefix . PropertyRegistry::getInstance()->findPropertyLabelById( $this->m_key );
194
	}
195
196
	/**
197
	 * @since 2.4
198
	 *
199
	 * @return string
200
	 */
201 60
	public function getCanonicalLabel() {
202 60
		$prefix = $this->m_inverse ? '-' : '';
203
204 60
		if ( $this->isUserDefined() ) {
205 54
			return $prefix . str_replace( '_', ' ', $this->m_key );
206
		}
207
208 13
		return $prefix . PropertyRegistry::getInstance()->findCanonicalPropertyLabelById( $this->m_key );
209
	}
210
211
	/**
212
	 * @since 2.4
213
	 *
214
	 * @param string $interwiki
215
	 */
216 3
	public function setInterwiki( $interwiki ) {
217 3
		$this->interwiki = $interwiki;
218 3
	}
219
220
	/**
221
	 * Get an object of type DIWikiPage that represents the page which
222
	 * relates to this property, or null if no such page exists. The latter
223
	 * can happen for special properties without user-readable label.
224
	 *
225
	 * It is possible to construct subobjects of the property's wikipage by
226
	 * providing an optional subobject name.
227
	 *
228
	 * @param string $subobjectName
229
	 * @return DIWikiPage|null
230
	 */
231 191
	public function getDiWikiPage( $subobjectName = '' ) {
232
233 191
		if ( $this->isUserDefined() ) {
234 181
			$dbkey = $this->m_key;
235
		} else {
236 129
			$dbkey = $this->getLabel();
237
		}
238
239 191
		return $this->newDIWikiPage( $dbkey, $subobjectName );
240
	}
241
242
	/**
243
	 * @since 2.4
244
	 *
245
	 * @param string $subobjectName
246
	 *
247
	 * @return DIWikiPage|null
248
	 */
249 106
	public function getCanonicalDiWikiPage( $subobjectName = '' ) {
250
251 106
		if ( $this->isUserDefined() ) {
252 92
			$dbkey = $this->m_key;
253
		} else {
254 33
			$dbkey = PropertyRegistry::getInstance()->findCanonicalPropertyLabelById( $this->m_key );
255
		}
256
257 106
		return $this->newDIWikiPage( $dbkey, $subobjectName );
258
	}
259
260
	/**
261
	 * @since 2.4
262
	 *
263
	 * @return DIProperty
264
	 */
265 177
	public function getRedirectTarget() {
266
267 177
		if ( $this->m_inverse ) {
268 8
			return $this;
269
		}
270
271 176
		return ApplicationFactory::getInstance()->getStore()->getRedirectTarget( $this );
272
	}
273
274
	/**
275
	 * @since  2.0
276
	 *
277
	 * @return self
278
	 * @throws RuntimeException
279
	 * @throws InvalidArgumentException
280
	 */
281 19
	public function setPropertyTypeId( $propertyTypeId ) {
282
283 19
		if ( !DataTypeRegistry::getInstance()->isKnownTypeId( $propertyTypeId ) ) {
284 1
			throw new RuntimeException( "{$propertyTypeId} is an unknown type id" );
285
		}
286
287 18
		if ( $this->isUserDefined() && $this->m_proptypeid === null ) {
288 16
			$this->m_proptypeid = $propertyTypeId;
289 16
			return $this;
290
		}
291
292 2
		if ( !$this->isUserDefined() && $propertyTypeId === self::getPredefinedPropertyTypeId( $this->m_key ) ) {
293 1
			$this->m_proptypeid = $propertyTypeId;
294 1
			return $this;
295
		}
296
297 1
		throw new InvalidArgumentException( 'Property type can not be altered for a predefined object' );
298
	}
299
300
	/**
301
	 * Find the property's type ID, either by looking up its predefined ID
302
	 * (if any) or by retrieving the relevant information from the store.
303
	 * If no type is stored for a user defined property, the global default
304
	 * type will be used.
305
	 *
306
	 * @return string type ID
307
	 */
308 232
	public function findPropertyTypeID() {
309 232
		global $smwgPDefaultType;
310
311 232
		if ( !isset( $this->m_proptypeid ) ) {
312 211
			if ( $this->isUserDefined() ) { // normal property
313 176
				$diWikiPage = new DIWikiPage( $this->getKey(), SMW_NS_PROPERTY, $this->interwiki );
314 176
				$typearray = ApplicationFactory::getInstance()->getStore()->getPropertyValues( $diWikiPage, new self( '_TYPE' ) );
315
316 176
				if ( count( $typearray ) >= 1 ) { // some types given, pick one (hopefully unique)
317 125
					$typeDataItem = reset( $typearray );
318
319 125
					if ( $typeDataItem instanceof SMWDIUri ) {
320 125
						$this->m_proptypeid = $typeDataItem->getFragment();
321
					} else {
322 125
						$this->m_proptypeid = $smwgPDefaultType;
323
						// This is important. If a page has an invalid assignment to "has type", no
324
						// value will be stored, so the elseif case below occurs. But if the value
325
						// is retrieved within the same run, then the error value for "has type" is
326
						// cached and thus this case occurs. This is why it is important to tolerate
327
						// this case -- it is not necessarily a DB error.
328
					}
329 172
				} elseif ( count( $typearray ) == 0 ) { // no type given
330 176
					$this->m_proptypeid = $smwgPDefaultType;
331
				}
332
			} else { // pre-defined property
333 201
				$this->m_proptypeid = PropertyRegistry::getInstance()->getPredefinedPropertyTypeId( $this->m_key );
334
			}
335
		}
336
337 232
		return $this->m_proptypeid;
338
	}
339
340
341 189
	public function getSerialization() {
342 189
		return ( $this->m_inverse ? '-' : '' ) . $this->m_key;
343
	}
344
345
	/**
346
	 * Create a data item from the provided serialization string and type
347
	 * ID.
348
	 *
349
	 * @param string $serialization
350
	 *
351
	 * @return DIProperty
352
	 */
353 14
	public static function doUnserialize( $serialization ) {
354 14
		$inverse = false;
355
356 14
		if ( $serialization{0} == '-' ) {
357
			$serialization = substr( $serialization, 1 );
358
			$inverse = true;
359
		}
360
361 14
		return new self( $serialization, $inverse );
362
	}
363
364
	/**
365
	 * @param SMWDataItem $di
366
	 *
367
	 * @return boolean
368
	 */
369 17
	public function equals( SMWDataItem $di ) {
370 17
		if ( $di->getDIType() !== SMWDataItem::TYPE_PROPERTY ) {
371 3
			return false;
372
		}
373
374 14
		return $di->getKey() === $this->m_key;
375
	}
376
377
	/**
378
	 * Construct a property from a user-supplied label. The main difference
379
	 * to the normal constructor of DIProperty is that it is checked
380
	 * whether the label refers to a known predefined property.
381
	 * Note that this function only gives access to the registry data that
382
	 * DIProperty stores, but does not do further parsing of user input.
383
	 *
384
	 * To process wiki input, SMWPropertyValue should be used.
385
	 *
386
	 * @param $label string label for the property
387
	 * @param $inverse boolean states if the inverse of the property is constructed
388
	 *
389
	 * @return DIProperty object
390
	 */
391 188
	public static function newFromUserLabel( $label, $inverse = false ) {
392
393 188
		if ( $label !== '' && $label{0} == '-' ) {
394 2
			$label = substr( $label, 1 );
395 2
			$inverse = true;
396
		}
397
398 188
		$id = false;
399
400
		// Special handling for when the user value contains a @LCODE marker
401 188
		if ( ( $langCode = Localizer::getLanguageCodeFrom( $label ) ) !== false ) {
402 3
			$id = PropertyRegistry::getInstance()->findPropertyIdByLanguageCode(
403
				$label,
404
				$langCode
405
			);
406
		}
407
408 188
		if ( $id !== false ) {
409 2
			return new self( $id, $inverse );
410
		}
411
412 188
		$id = PropertyRegistry::getInstance()->findPropertyIdByLabel(
413 188
			str_replace( '_', ' ', $label )
414
		);
415
416 188
		if ( $id === false ) {
417 178
			return new self( str_replace( ' ', '_', $label ), $inverse );
418
		}
419
420 140
		return new self( $id, $inverse );
421
	}
422
423
	/**
424
	 * @deprecated since 2.1, use PropertyRegistry::findPropertyIdByLabel
425
	 */
426
	public static function findPropertyID( $label, $useAlias = true ) {
427
		return PropertyRegistry::getInstance()->findPropertyIdByLabel( $label, $useAlias );
428
	}
429
430
	/**
431
	 * @deprecated since 2.1, use PropertyRegistry::getPredefinedPropertyTypeId
432
	 */
433
	public static function getPredefinedPropertyTypeId( $key ) {
434
		return PropertyRegistry::getInstance()->getPredefinedPropertyTypeId( $key );
435
	}
436
437
	/**
438
	 * @deprecated since 2.1, use PropertyRegistry::findPropertyLabelById
439
	 */
440
	static public function findPropertyLabel( $id ) {
0 ignored issues
show
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
441
		return PropertyRegistry::getInstance()->findPropertyLabel( $id );
442
	}
443
444
	/**
445
	 * @deprecated since 2.1, use PropertyRegistry::registerProperty
446
	 */
447
	static public function registerProperty( $id, $typeid, $label = false, $show = false ) {
448
		PropertyRegistry::getInstance()->registerProperty( $id, $typeid, $label, $show);
449
	}
450
451
	/**
452
	 * @deprecated since 2.1, use PropertyRegistry::registerPropertyAlias
453
	 */
454
	static public function registerPropertyAlias( $id, $label ) {
455
		PropertyRegistry::getInstance()->registerPropertyAlias( $id, $label );
456
	}
457
458 199
	private function newDIWikiPage( $dbkey, $subobjectName ) {
459
460
		// If an inverse marker is present just omit the marker so a normal
461
		// property page link can be produced independent of its directionality
462 199
		if ( $dbkey !== '' && $dbkey{0} == '-'  ) {
463 3
			$dbkey = substr( $dbkey, 1 );
464
		}
465
466
		try {
467 199
			return new DIWikiPage( str_replace( ' ', '_', $dbkey ), SMW_NS_PROPERTY, $this->interwiki, $subobjectName );
468
		} catch ( DataItemException $e ) {
469
			return null;
470
		}
471
	}
472
473
}
474