Completed
Push — master ( 5d1976...30add5 )
by mw
13s
created

includes/dataitems/SMW_DI_Property.php (1 issue)

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 250
	public function __construct( $key, $inverse = false ) {
81 250
		if ( ( $key === '' ) || ( $key{0} == '-' ) ) {
82
			throw new InvalidPropertyException( "Illegal property key \"$key\"." );
83
		}
84
85 250
		if ( $key{0} == '_' ) {
86 238
			if ( !PropertyRegistry::getInstance()->isKnownPropertyId( $key ) ) {
87 1
				throw new InvalidPredefinedPropertyException( "There is no predefined property with \"$key\"." );
88
			}
89
		}
90
91 249
		$this->m_key = $key;
92 249
		$this->m_inverse = $inverse;
93 249
	}
94
95
	/**
96
	 * @return integer
97
	 */
98 180
	public function getDIType() {
99 180
		return SMWDataItem::TYPE_PROPERTY;
100
	}
101
102
	/**
103
	 * @return string
104
	 */
105 239
	public function getKey() {
106 239
		return $this->m_key;
107
	}
108
109
	/**
110
	 * @return boolean
111
	 */
112 235
	public function isInverse() {
113 235
		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 192
	public function isShown() {
141
142 192
		if ( $this->isUserDefined() ) {
143 1
			return true;
144
		}
145
146 192
		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 234
	public function isUserDefined() {
156 234
		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 146
	public function isUnrestrictedForUse() {
171
172 146
		if ( $this->isUserDefined() ) {
173 140
			return true;
174
		}
175
176 113
		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 218
	public function getLabel() {
187 218
		$prefix = $this->m_inverse ? '-' : '';
188
189 218
		if ( $this->isUserDefined() ) {
190 171
			return $prefix . str_replace( '_', ' ', $this->m_key );
191
		}
192
193 216
		return $prefix . PropertyRegistry::getInstance()->findPropertyLabelById( $this->m_key );
194
	}
195
196
	/**
197
	 * @since 2.4
198
	 *
199
	 * @param string $interwiki
200
	 */
201 3
	public function setInterwiki( $interwiki ) {
202 3
		$this->interwiki = $interwiki;
203 3
	}
204
205
	/**
206
	 * Get an object of type DIWikiPage that represents the page which
207
	 * relates to this property, or null if no such page exists. The latter
208
	 * can happen for special properties without user-readable label.
209
	 *
210
	 * It is possible to construct subobjects of the property's wikipage by
211
	 * providing an optional subobject name.
212
	 *
213
	 * @param string $subobjectName
214
	 * @return DIWikiPage|null
215
	 */
216 182
	public function getDiWikiPage( $subobjectName = '' ) {
217
218 182
		if ( $this->isUserDefined() ) {
219 169
			$dbkey = $this->m_key;
220
		} else {
221 118
			$dbkey = $this->getLabel();
222
		}
223
224 182
		return $this->newDIWikiPage( $dbkey, $subobjectName );
225
	}
226
227
	/**
228
	 * @since 2.4
229
	 *
230
	 * @param string $subobjectName
231
	 *
232
	 * @return DIWikiPage|null
233
	 */
234 10
	public function getCanonicalDiWikiPage( $subobjectName = '' ) {
235
236 10
		if ( $this->isUserDefined() ) {
237 9
			$dbkey = $this->m_key;
238
		} else {
239 9
			$dbkey = PropertyRegistry::getInstance()->findCanonicalPropertyLabelById( $this->m_key );
240
		}
241
242 10
		return $this->newDIWikiPage( $dbkey, $subobjectName );
243
	}
244
245
	/**
246
	 * @since 2.4
247
	 *
248
	 * @return DIProperty
249
	 */
250 164
	public function getRedirectTarget() {
251
252 164
		if ( $this->m_inverse ) {
253 8
			return $this;
254
		}
255
256 163
		return ApplicationFactory::getInstance()->getStore()->getRedirectTarget( $this );
257
	}
258
259
	/**
260
	 * @since  2.0
261
	 *
262
	 * @return self
263
	 * @throws RuntimeException
264
	 * @throws InvalidArgumentException
265
	 */
266 19
	public function setPropertyTypeId( $propertyTypeId ) {
267
268 19
		if ( !DataTypeRegistry::getInstance()->isKnownTypeId( $propertyTypeId ) ) {
269 1
			throw new RuntimeException( "{$propertyTypeId} is an unknown type id" );
270
		}
271
272 18
		if ( $this->isUserDefined() && $this->m_proptypeid === null ) {
273 16
			$this->m_proptypeid = $propertyTypeId;
274 16
			return $this;
275
		}
276
277 2
		if ( !$this->isUserDefined() && $propertyTypeId === self::getPredefinedPropertyTypeId( $this->m_key ) ) {
278 1
			$this->m_proptypeid = $propertyTypeId;
279 1
			return $this;
280
		}
281
282 1
		throw new InvalidArgumentException( 'Property type can not be altered for a predefined object' );
283
	}
284
285
	/**
286
	 * Find the property's type ID, either by looking up its predefined ID
287
	 * (if any) or by retrieving the relevant information from the store.
288
	 * If no type is stored for a user defined property, the global default
289
	 * type will be used.
290
	 *
291
	 * @return string type ID
292
	 */
293 215
	public function findPropertyTypeID() {
294 215
		global $smwgPDefaultType;
295
296 215
		if ( !isset( $this->m_proptypeid ) ) {
297 194
			if ( $this->isUserDefined() ) { // normal property
298 160
				$diWikiPage = new DIWikiPage( $this->getKey(), SMW_NS_PROPERTY, $this->interwiki );
299 160
				$typearray = ApplicationFactory::getInstance()->getStore()->getPropertyValues( $diWikiPage, new self( '_TYPE' ) );
300
301 160
				if ( count( $typearray ) >= 1 ) { // some types given, pick one (hopefully unique)
302 112
					$typeDataItem = reset( $typearray );
303
304 112
					if ( $typeDataItem instanceof SMWDIUri ) {
305 112
						$this->m_proptypeid = $typeDataItem->getFragment();
306
					} else {
307 112
						$this->m_proptypeid = $smwgPDefaultType;
308
						// This is important. If a page has an invalid assignment to "has type", no
309
						// value will be stored, so the elseif case below occurs. But if the value
310
						// is retrieved within the same run, then the error value for "has type" is
311
						// cached and thus this case occurs. This is why it is important to tolerate
312
						// this case -- it is not necessarily a DB error.
313
					}
314 156
				} elseif ( count( $typearray ) == 0 ) { // no type given
315 160
					$this->m_proptypeid = $smwgPDefaultType;
316
				}
317
			} else { // pre-defined property
318 184
				$this->m_proptypeid = PropertyRegistry::getInstance()->getPredefinedPropertyTypeId( $this->m_key );
319
			}
320
		}
321
322 215
		return $this->m_proptypeid;
323
	}
324
325
326 172
	public function getSerialization() {
327 172
		return ( $this->m_inverse ? '-' : '' ) . $this->m_key;
328
	}
329
330
	/**
331
	 * Create a data item from the provided serialization string and type
332
	 * ID.
333
	 *
334
	 * @param string $serialization
335
	 *
336
	 * @return DIProperty
337
	 */
338 14
	public static function doUnserialize( $serialization ) {
339 14
		$inverse = false;
340
341 14
		if ( $serialization{0} == '-' ) {
342
			$serialization = substr( $serialization, 1 );
343
			$inverse = true;
344
		}
345
346 14
		return new self( $serialization, $inverse );
347
	}
348
349
	/**
350
	 * @param SMWDataItem $di
351
	 *
352
	 * @return boolean
353
	 */
354 17
	public function equals( SMWDataItem $di ) {
355 17
		if ( $di->getDIType() !== SMWDataItem::TYPE_PROPERTY ) {
356 3
			return false;
357
		}
358
359 14
		return $di->getKey() === $this->m_key;
360
	}
361
362
	/**
363
	 * Construct a property from a user-supplied label. The main difference
364
	 * to the normal constructor of DIProperty is that it is checked
365
	 * whether the label refers to a known predefined property.
366
	 * Note that this function only gives access to the registry data that
367
	 * DIProperty stores, but does not do further parsing of user input.
368
	 *
369
	 * To process wiki input, SMWPropertyValue should be used.
370
	 *
371
	 * @param $label string label for the property
372
	 * @param $inverse boolean states if the inverse of the property is constructed
373
	 *
374
	 * @return DIProperty object
375
	 */
376 171
	public static function newFromUserLabel( $label, $inverse = false ) {
377
378 171
		if ( $label !== '' && $label{0} == '-' ) {
379 2
			$label = substr( $label, 1 );
380 2
			$inverse = true;
381
		}
382
383 171
		$id = false;
384
385
		// Special handling for when the user value contains a @LCODE marker
386 171
		if ( ( $langCode = Localizer::getLanguageCodeFrom( $label ) ) !== false ) {
387 2
			$id = PropertyRegistry::getInstance()->findPropertyIdByLanguageCode(
388
				$label,
389
				$langCode
390
			);
391
		}
392
393 171
		if ( $id !== false ) {
394 1
			return new self( $id, $inverse );
395
		}
396
397 171
		$id = PropertyRegistry::getInstance()->findPropertyIdByLabel(
398 171
			str_replace( '_', ' ', $label )
399
		);
400
401 171
		if ( $id === false ) {
402 162
			return new self( str_replace( ' ', '_', $label ), $inverse );
403
		}
404
405 124
		return new self( $id, $inverse );
406
	}
407
408
	/**
409
	 * @deprecated since 2.1, use PropertyRegistry::findPropertyIdByLabel
410
	 */
411
	public static function findPropertyID( $label, $useAlias = true ) {
412
		return PropertyRegistry::getInstance()->findPropertyIdByLabel( $label, $useAlias );
413
	}
414
415
	/**
416
	 * @deprecated since 2.1, use PropertyRegistry::getPredefinedPropertyTypeId
417
	 */
418
	public static function getPredefinedPropertyTypeId( $key ) {
419
		return PropertyRegistry::getInstance()->getPredefinedPropertyTypeId( $key );
420
	}
421
422
	/**
423
	 * @deprecated since 2.1, use PropertyRegistry::findPropertyLabelById
424
	 */
425
	static public function findPropertyLabel( $id ) {
0 ignored issues
show
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
426
		return PropertyRegistry::getInstance()->findPropertyLabel( $id );
427
	}
428
429
	/**
430
	 * @deprecated since 2.1, use PropertyRegistry::registerProperty
431
	 */
432
	static public function registerProperty( $id, $typeid, $label = false, $show = false ) {
433
		PropertyRegistry::getInstance()->registerProperty( $id, $typeid, $label, $show);
434
	}
435
436
	/**
437
	 * @deprecated since 2.1, use PropertyRegistry::registerPropertyAlias
438
	 */
439
	static public function registerPropertyAlias( $id, $label ) {
440
		PropertyRegistry::getInstance()->registerPropertyAlias( $id, $label );
441
	}
442
443 182
	private function newDIWikiPage( $dbkey, $subobjectName ) {
444
445
		// If an inverse marker is present just omit the marker so a normal
446
		// property page link can be produced independent of its directionality
447 182
		if ( $dbkey !== '' && $dbkey{0} == '-'  ) {
448 3
			$dbkey = substr( $dbkey, 1 );
449
		}
450
451
		try {
452 182
			return new DIWikiPage( str_replace( ' ', '_', $dbkey ), SMW_NS_PROPERTY, $this->interwiki, $subobjectName );
453
		} catch ( DataItemException $e ) {
454
			return null;
455
		}
456
	}
457
458
}
459