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

includes/datavalues/SMW_DV_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
use SMW\ApplicationFactory;
4
use SMW\DataValueFactory;
5
use SMW\DIProperty;
6
use SMW\Highlighter;
7
8
/**
9
 * Objects of this class represent properties in SMW.
10
 *
11
 * This class represents both normal (user-defined) properties and
12
 * predefined ("special") properties. Predefined properties may still
13
 * have a standard label (and associated wiki article) and they will
14
 * behave just like user-defined properties in most cases (e.g. when
15
 * asking for a printout text, a link to the according page is produced).
16
 * It is possible that predefined properties have no visible label at all,
17
 * if they are used only internally and never specified by or shown to
18
 * the user. Those will use their internal ID as DB key, and
19
 * empty texts for most printouts. All other proeprties use their
20
 * canonical DB key (even if they are predefined and have an id).
21
 * Functions are provided to check whether a property is visible or
22
 * user-defined, and to get the internal ID, if any.
23
 *
24
 * @note This datavalue is used only for representing properties and,
25
 * possibly objects/values, but never for subjects (pages as such). Hence
26
 * it does not provide a complete Title-like interface, or support for
27
 * things like sortkey.
28
 *
29
 * @author Markus Krötzsch
30
 * @ingroup SMWDataValues
31
 */
32
class SMWPropertyValue extends SMWDataValue {
33
34
	/**
35
	 * Cache for wiki page value object associated to this property, or
36
	 * null if no such page exists. Use getWikiPageValue() to get the data.
37
	 * @var SMWWikiPageValue
38
	 */
39
	protected $m_wikipage = null;
40
41
	/**
42
	 * @var array
43
	 */
44
	protected $linkAttributes = array();
45
46
	/**
47
	 * Cache for type value of this property, or null if not calculated yet.
48
	 * @var SMWTypesValue
49
	 */
50
	private $mPropTypeValue;
51
52
	/**
53
	 * @var DIProperty
54
	 */
55
	private $inceptiveProperty = null;
56
57
	/**
58
	 * @since 2.4
59
	 *
60
	 * @param string $typeid
61
	 */
62 181
	public function __construct( $typeid ) {
63 181
		parent::__construct( $typeid );
64 181
	}
65
66
	/**
67
	 * Static function for creating a new property object from a
68
	 * propertyname (string) as a user might enter it.
69
	 * @note The resulting property object might be invalid if
70
	 * the provided name is not allowed. An object is returned
71
	 * in any case.
72
	 *
73
	 * @param string $propertyName
74
	 *
75
	 * @return SMWPropertyValue
76
	 */
77 105
	static public function makeUserProperty( $propertyLabel ) {
78 105
		return DataValueFactory::getInstance()->newPropertyValueByLabel( $propertyLabel );
79
	}
80
81
	/**
82
	 * Static function for creating a new property object from a property
83
	 * identifier (string) as it might be used internally. This might be
84
	 * the DB key version of some property title text or the id of a
85
	 * predefined property (such as '_TYPE').
86
	 * @note This function strictly requires an internal identifier, i.e.
87
	 * predefined properties must be referred to by their ID, and '-' is
88
	 * not supported for indicating inverses.
89
	 * @note The resulting property object might be invalid if
90
	 * the provided name is not allowed. An object is returned
91
	 * in any case.
92
	 */
93
	static public function makeProperty( $propertyid ) {
94
		$diProperty = new DIProperty( $propertyid );
95
		$dvProperty = new SMWPropertyValue( '__pro' );
96
		$dvProperty->setDataItem( $diProperty );
97
		return $dvProperty;
98
	}
99
100
	/**
101
	 * We use the internal wikipage object to store some of this objects data.
102
	 * Clone it to make sure that data can be modified independently from the
103
	 * original object's content.
104
	 */
105 1
	public function __clone() {
106 1
		if ( !is_null( $this->m_wikipage ) ) {
107 1
			$this->m_wikipage = clone $this->m_wikipage;
108
		}
109 1
	}
110
111
	/**
112
	 * @note If the inceptive property and the property referenced in dataItem
113
	 * are not equal then the dataItem represents the end target to which the
114
	 * inceptive property has been redirected.
115
	 *
116
	 * @since 2.4
117
	 *
118
	 * @return DIProperty
119
	 */
120 1
	public function getInceptiveProperty() {
121 1
		return $this->inceptiveProperty;
122
	}
123
124
	/**
125
	 * Extended parsing function to first check whether value refers to pre-defined
126
	 * property, resolve aliases, and set internal property id accordingly.
127
	 * @todo Accept/enforce property namespace.
128
	 */
129 162
	protected function parseUserValue( $value ) {
130 162
		$this->mPropTypeValue = null;
131 162
		$this->m_wikipage = null;
132
133 162
		if ( $this->m_caption === false ) { // always use this as caption
134 162
			$this->m_caption = $value;
135
		}
136 162
		$propertyName = smwfNormalTitleText( ltrim( rtrim( $value, ' ]' ), ' [' ) ); // slightly normalise label
137 162
		$inverse = false;
138 162
		if ( ( $propertyName !== '' ) && ( $propertyName { 0 } == '-' ) ) { // property refers to an inverse
139 8
			$propertyName = smwfNormalTitleText( (string)substr( $value, 1 ) );
140
			/// NOTE The cast is necessary at least in PHP 5.3.3 to get string '' instead of boolean false.
141
			/// NOTE It is necessary to normalize again here, since normalization may uppercase the first letter.
142 8
			$inverse = true;
143
		}
144
145
		try {
146 162
			$this->m_dataitem = DIProperty::newFromUserLabel( $propertyName, $inverse, $this->m_typeid );
147
		} catch ( SMWDataItemException $e ) { // happens, e.g., when trying to sort queries by property "-"
148
			$this->addError( wfMessage( 'smw_noproperty', $value )->inContentLanguage()->text() );
149
			$this->m_dataitem = new DIProperty( 'ERROR', false ); // just to have something
150
		}
151
152
		// @see the SMW_DV_PROV_DTITLE explanation
153 162
		if ( $this->isEnabledFeature( SMW_DV_PROV_DTITLE ) ) {
154
			$dataItem = $this->getPropertySpecificationLookup()->getPropertyFromDisplayTitle(
155
				$value
156
			);
157
158
			$this->m_dataitem = $dataItem ? $dataItem : $this->m_dataitem;
159
		}
160
161 162
		$this->inceptiveProperty = $this->m_dataitem;
162
163 162
		if ( $this->isEnabledFeature( SMW_DV_PROV_REDI ) ) {
164 160
			$this->m_dataitem = $this->m_dataitem->getRedirectTarget();
165
		}
166 162
	}
167
168
	/**
169
	 * @see SMWDataValue::loadDataItem()
170
	 * @param $dataitem SMWDataItem
171
	 * @return boolean
172
	 */
173 24
	protected function loadDataItem( SMWDataItem $dataItem ) {
174
175 24
		if ( $dataItem->getDIType() !== SMWDataItem::TYPE_PROPERTY ) {
176
			return false;
177
		}
178
179 24
		$this->inceptiveProperty = $dataItem;
180 24
		$this->m_dataitem = $dataItem;
181
182 24
		$this->mPropTypeValue = null;
183 24
		unset( $this->m_wikipage );
184 24
		$this->m_caption = false;
185 24
		$this->linkAttributes = array();
186
187 24
		return true;
188
	}
189
190
	/**
191
	 * @since 2.4
192
	 *
193
	 * @param array $linkAttributes
194
	 */
195
	public function setLinkAttributes( array $setLinkAttributes ) {
196
		$this->linkAttributes = $linkAttributes;
197
198
		if ( $this->getWikiPageValue() instanceof SMWDataValue ) {
199
			$this->m_wikipage->setLinkAttributes( $linkAttributes );
200
		}
201
	}
202
203 82
	public function setCaption( $caption ) {
204 82
		parent::setCaption( $caption );
205 82
		if ( $this->getWikiPageValue() instanceof SMWDataValue ) { // pass caption to embedded datavalue (used for printout)
206 82
			$this->m_wikipage->setCaption( $caption );
207
		}
208 82
	}
209
210
	public function setOutputFormat( $formatstring ) {
211
		$this->m_outformat = $formatstring;
212
		if ( $this->m_wikipage instanceof SMWDataValue ) {
213
			$this->m_wikipage->setOutputFormat( $formatstring );
214
		}
215
	}
216
217
	public function setInverse( $isinverse ) {
218
		return $this->m_dataitem = new DIProperty( $this->m_dataitem->getKey(), ( $isinverse == true ) );
219
	}
220
221
	/**
222
	 * Return a wiki page value that can be used for displaying this
223
	 * property, or null if no such wiki page exists (for predefined
224
	 * properties without any label).
225
	 * @return SMWWikiPageValue or null
226
	 */
227 85
	public function getWikiPageValue() {
228 85
		if ( !isset( $this->m_wikipage ) ) {
229
230 85
			$diWikiPage = $this->m_dataitem->getDiWikiPage();
231
232 85
			if ( $diWikiPage !== null ) {
233 85
				$this->m_wikipage = \SMW\DataValueFactory::getInstance()->newDataItemValue( $diWikiPage, null, $this->m_caption );
234 85
				$this->m_wikipage->setOutputFormat( $this->m_outformat );
235 85
				$this->m_wikipage->setLinkAttributes( $this->linkAttributes );
236 85
				$this->addError( $this->m_wikipage->getErrors() );
237
			} else { // should rarely happen ($value is only changed if the input $value really was a label for a predefined prop)
238
				$this->m_wikipage = null;
239
			}
240
		}
241
242 85
		return $this->m_wikipage;
243
	}
244
245
	/**
246
	 * Return TRUE if this is a property that can be displayed, and not a pre-defined
247
	 * property that is used only internally and does not even have a user-readable name.
248
	 * @note Every user defined property is necessarily visible.
249
	 */
250 54
	public function isVisible() {
251 54
		return $this->isValid() && ( $this->m_dataitem->isUserDefined() || $this->m_dataitem->getLabel() !== '' );
252
	}
253
254
	/**
255
	 * @since 2.2
256
	 *
257
	 * @return boolean
258
	 */
259 146
	public function canUse() {
260 146
		return $this->isValid() && $this->m_dataitem->isUnrestrictedForUse();
261
	}
262
263 28
	public function getShortWikiText( $linked = null ) {
264
265 28
		if ( $this->isVisible() ) {
266 28
			$wikiPageValue = $this->getWikiPageValue();
267 28
			return is_null( $wikiPageValue ) ? '' : $this->highlightText( $wikiPageValue->getShortWikiText( $linked ) );
268
		}
269
270
		return '';
271
	}
272
273 2
	public function getShortHTMLText( $linked = null ) {
274
275 2
		if ( $this->isVisible() ) {
276 2
			$wikiPageValue = $this->getWikiPageValue();
277 2
			return is_null( $wikiPageValue ) ? '' : $this->highlightText( $wikiPageValue->getShortHTMLText( $linked ), $linked );
278
		}
279
280
		return '';
281
	}
282
283 8
	public function getLongWikiText( $linked = null ) {
284
285 8
		if ( $this->isVisible() ) {
286 8
			$wikiPageValue = $this->getWikiPageValue();
287 8
			return is_null( $wikiPageValue ) ? '' : $this->highlightText( $wikiPageValue->getLongWikiText( $linked ) );
288
		}
289
290
		return '';
291
	}
292
293
	public function getLongHTMLText( $linked = null ) {
294
295
		if ( $this->isVisible() ) {
296
			$wikiPageValue = $this->getWikiPageValue();
297
			return is_null( $wikiPageValue ) ? '' : $this->highlightText( $wikiPageValue->getLongHTMLText( $linked ), $linked );
298
		}
299
300
		return '';
301
	}
302
303 48
	public function getWikiValue() {
304
305 48
		if ( !$this->isVisible() ) {
306
			return '';
307
		}
308
309 48
		if ( $this->getWikiPageValue() !== null && $this->getWikiPageValue()->getDisplayTitle() !== '' ) {
310 1
			return $this->getWikiPageValue()->getDisplayTitle();
311
		}
312
313 48
		return $this->m_dataitem->getLabel();
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class SMWDataItem as the method getLabel() does only exist in the following sub-classes of SMWDataItem: SMWDIProperty, SMW\DIProperty. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
314
	}
315
316
	/**
317
	 * If this property was not user defined, return the internal ID string referring to
318
	 * that property. Otherwise return FALSE;
319
	 */
320
	public function getPropertyID() {
321
		return $this->m_dataitem->isUserDefined() ? false : $this->m_dataitem->getKey();
322
	}
323
324
	/**
325
	 * Return an SMWTypesValue object representing the datatype of this
326
	 * property.
327
	 * @deprecated Types values are not a good way to exchange SMW type information. They are for input only. Use getPropertyTypeID() if you want the type id. This method will vanish in SMW 1.7.
328
	 */
329
	public function getTypesValue() {
330
		$result = SMWTypesValue::newFromTypeId( $this->getPropertyTypeID() );
331
		if ( !$this->isValid() ) {
332
			$result->addError( $this->getErrors() );
333
		}
334
		return $result;
335
	}
336
337
	/**
338
	 * Convenience method to find the type id of this property. Most callers
339
	 * should rather use DIProperty::findPropertyTypeId() directly. Note
340
	 * that this is not the same as getTypeID(), which returns the id of
341
	 * this property datavalue.
342
	 *
343
	 * @return string
344
	 */
345 4
	public function getPropertyTypeID() {
346 4
		if ( $this->isValid() ) {
347 4
			return $this->m_dataitem->findPropertyTypeId();
348
		} else {
349
			return '__err';
350
		}
351
	}
352
353
	/**
354
	 * Create special highlighting for hinting at special properties.
355
	 */
356 35
	protected function highlightText( $text, $linker = null ) {
357
358 35
		$propertySpecificationLookup = ApplicationFactory::getInstance()->getPropertySpecificationLookup();
359
360 35
		if ( ( $content = $propertySpecificationLookup->getPropertyDescriptionFor( $this->m_dataitem, $linker ) ) !== '' ) {
361
362 6
			$highlighter = Highlighter::factory( Highlighter::TYPE_PROPERTY );
363 6
			$highlighter->setContent( array (
364 6
				'userDefined' => $this->m_dataitem->isUserDefined(),
365 6
				'caption' => $text,
366 6
				'content' => $content !== '' ? $content : wfMessage( 'smw_isspecprop' )->text()
367
			) );
368
369 6
			return $highlighter->getHtml();
370
		}
371
372 31
		return $text;
373
	}
374
375
	/**
376
	 * A function for registering/overwriting predefined properties for SMW. Should be called from
377
	 * within the hook 'smwInitProperties'. Ids should start with three underscores "___" to avoid
378
	 * current and future confusion with SMW built-ins.
379
	 *
380
	 * @deprecated Use DIProperty::registerProperty(). Will vanish before SMW 1.7.
381
	 */
382
	static public function registerProperty( $id, $typeid, $label = false, $show = false ) {
383
		DIProperty::registerProperty( $id, $typeid, $label, $show );
384
	}
385
386
	/**
387
	 * Add a new alias label to an existing datatype id. Note that every ID should have a primary
388
	 * label, either provided by SMW or registered with registerDatatype. This function should be
389
	 * called from within the hook 'smwInitDatatypes'.
390
	 *
391
	 * @deprecated Use DIProperty::registerPropertyAlias(). Will vanish before SMW 1.7.
392
	 */
393
	static public function registerPropertyAlias( $id, $label ) {
394
		DIProperty::registerPropertyAlias( $id, $label );
395
	}
396
397
	/**
398
	 * @see DIProperty::isUserDefined()
399
	 *
400
	 * @deprecated since 1.6
401
	 */
402
	public function isUserDefined() {
403
		return $this->m_dataitem->isUserDefined();
404
	}
405
406
	/**
407
	 * @see DIProperty::isShown()
408
	 *
409
	 * @deprecated since 1.6
410
	 */
411
	public function isShown() {
412
		return $this->m_dataitem->isShown();
413
	}
414
415
	/**
416
	 * @see DIProperty::isInverse()
417
	 *
418
	 * @deprecated since 1.6
419
	 */
420
	public function isInverse() {
421
		return $this->m_dataitem->isInverse();
422
	}
423
424
	/**
425
	 * Return a DB-key-like string: for visible properties, it is the actual DB key,
426
	 * for internal (invisible) properties, it is the property ID. The value agrees
427
	 * with the first component of getDBkeys() and it can be used in its place.
428
	 * @see DIProperty::getKey()
429
	 *
430
	 * @deprecated since 1.6
431
	 */
432
	public function getDBkey() {
433
		return $this->m_dataitem->getKey();
434
	}
435
436
	/**
437
	 * @see DIProperty::getLabel()
438
	 *
439
	 * @deprecated since 1.6
440
	 */
441
	public function getText() {
442
		return $this->m_dataitem->getLabel();
443
	}
444
445
}
446