Completed
Push — master ( 9eae06...a41cb5 )
by mw
16s
created

src/DataValues/MonolingualTextValue.php (4 issues)

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\DataValues;
4
5
use SMW\ApplicationFactory;
6
use SMW\DataValueFactory;
7
use SMW\DataValues\ValueFormatters\DataValueFormatter;
8
use SMW\DataValues\ValueParsers\MonolingualTextValueParser;
9
use SMW\DIProperty;
10
use SMW\DIWikiPage;
11
use SMW\Localizer;
12
use SMWContainerSemanticData as ContainerSemanticData;
13
use SMWDataItem as DataItem;
14
use SMWDataValue as DataValue;
15
use SMWDIContainer as DIContainer;
16
17
/**
18
 * MonolingualTextValue requires two components, a language code and a
19
 * text.
20
 *
21
 * A text `foo@en` is expected to be invoked with a BCP47 language
22
 * code tag and a language dependent text component.
23
 *
24
 * Internally, the value is stored as container object that represents
25
 * the language code and text as separate entities in order to be queried
26
 * individually.
27
 *
28
 * External output representation depends on the context (wiki, html)
29
 * whether the language code is omitted or not.
30
 *
31
 * @license GNU GPL v2+
32
 * @since 2.4
33
 *
34
 * @author mwjames
35
 */
36
class MonolingualTextValue extends AbstractMultiValue {
37
38
	/**
39
	 * @var DIProperty[]|null
40
	 */
41
	private static $properties = null;
42
43
	/**
44
	 * @var MonolingualTextValueParser
45
	 */
46
	private $monolingualTextValueParser = null;
47
48
	/**
49
	 * @param string $typeid
50
	 */
51 19
	public function __construct( $typeid = '' ) {
52 19
		parent::__construct( '_mlt_rec' );
53 19
	}
54
55
	/**
56
	 * @see AbstractMultiValue::setFieldProperties
57
	 *
58
	 * @param DIProperty[] $properties
59
	 */
60
	public function setFieldProperties( array $properties ) {
61
		// Keep the interface while the properties for this type
62
		// are fixed.
63
	}
64
65
	/**
66
	 * @see AbstractMultiValue::getProperties
67
	 *
68
	 * @param DIProperty[] $properties
69
	 */
70
	public function getProperties() {
71
		self::$properties;
72
	}
73
74
	/**
75
	 * @see DataValue::parseUserValue
76
	 * @note called by DataValue::setUserValue
77
	 *
78
	 * @param string $userValue
79
	 */
80 17
	protected function parseUserValue( $userValue ) {
81
82 17
		list( $text, $languageCode ) = $this->getValuesFromString( $userValue );
83
84 17
		$languageCodeValue = $this->newLanguageCodeValue( $languageCode );
85
86
		if (
87 17
			( $languageCode !== '' && $languageCodeValue->getErrors() !== array() ) ||
88 17
			( $languageCode === '' && $this->isEnabledFeature( SMW_DV_MLTV_LCODE ) ) ) {
89 3
			$this->addError( $languageCodeValue->getErrors() );
90 3
			return;
91
		}
92
93 14
		$dataValues = array();
94
95 14
		foreach ( $this->getPropertyDataItems() as $property ) {
96
97
			if (
98 14
				( $languageCode === '' && $property->getKey() === '_LCODE' ) ||
99 14
				( $text === '' && $property->getKey() === '_TEXT' ) ) {
100 1
				continue;
101
			}
102
103 14
			$value = $text;
104
105 14
			if ( $property->getKey() === '_LCODE' ) {
106 13
				$value = $languageCode;
107
			}
108
109 14
			$dataValue = DataValueFactory::getInstance()->newDataValueByProperty(
110
				$property,
111
				$value,
112 14
				false,
113 14
				$this->m_contextPage
114
			);
115
116 14
			$dataValues[] = $dataValue;
117
		}
118
119
		// Generate a hash from the normalized representation so that foo@en being
120
		// the same as foo@EN independent of a user input
121 14
		$containerSemanticData = $this->newContainerSemanticData( $text . '@' . $languageCode );
122
123 14
		foreach ( $dataValues as $dataValue ) {
124 14
			$containerSemanticData->addDataValue( $dataValue );
125
		}
126
127 14
		$this->m_dataitem = new DIContainer( $containerSemanticData );
128 14
	}
129
130
	/**
131
	 * @note called by MonolingualTextValueDescriptionDeserializer::deserialize
132
	 * and MonolingualTextValue::parseUserValue
133
	 *
134
	 * No explicit check is made on the validity of a language code and is
135
	 * expected to be done before calling this method.
136
	 *
137
	 * @since 2.4
138
	 *
139
	 * @param string $userValue
140
	 *
141
	 * @return array
142
	 */
143 17
	public function getValuesFromString( $userValue ) {
144 17
		return $this->getValueParser()->parse( $userValue );
145
	}
146
147
	/**
148
	 * @see DataValue::loadDataItem
149
	 *
150
	 * @param DataItem $dataItem
151
	 *
152
	 * @return boolean
153
	 */
154 6
	protected function loadDataItem( DataItem $dataItem ) {
155
156 6
		if ( $dataItem->getDIType() === DataItem::TYPE_CONTAINER ) {
157
			$this->m_dataitem = $dataItem;
158
			return true;
159 6
		} elseif ( $dataItem->getDIType() === DataItem::TYPE_WIKIPAGE ) {
160 6
			$semanticData = new ContainerSemanticData( $dataItem );
161 6
			$semanticData->copyDataFrom( ApplicationFactory::getInstance()->getStore()->getSemanticData( $dataItem ) );
162 6
			$this->m_dataitem = new DIContainer( $semanticData );
163 6
			return true;
164
		}
165
166 2
		return false;
167
	}
168
169
	/**
170
	 * @see DataValue::getShortWikiText
171
	 */
172 10
	public function getShortWikiText( $linker = null ) {
173 10
		return $this->getDataValueFormatter()->format( DataValueFormatter::WIKI_SHORT, $linker );
174
	}
175
176
	/**
177
	 * @see DataValue::getShortHTMLText
178
	 */
179
	public function getShortHTMLText( $linker = null ) {
180
		return $this->getDataValueFormatter()->format( DataValueFormatter::HTML_SHORT, $linker );
181
	}
182
183
	/**
184
	 * @see DataValue::getLongWikiText
185
	 */
186
	public function getLongWikiText( $linker = null ) {
187
		return $this->getDataValueFormatter()->format( DataValueFormatter::WIKI_LONG, $linker );
188
	}
189
190
	/**
191
	 * @see DataValue::getLongHTMLText
192
	 */
193
	public function getLongHTMLText( $linker = null ) {
194
		return $this->getDataValueFormatter()->format( DataValueFormatter::HTML_LONG, $linker );
195
	}
196
197
	/**
198
	 * @see DataValue::getWikiValue
199
	 */
200 5
	public function getWikiValue() {
201 5
		return $this->getDataValueFormatter()->format( DataValueFormatter::VALUE );
202
	}
203
204
	/**
205
	 * @since 2.4
206
	 * @note called by AbstractRecordValue::getPropertyDataItems
207
	 *
208
	 * @return DIProperty[]
209
	 */
210 15
	public function getPropertyDataItems() {
211
212 15
		if ( self::$properties !== null && self::$properties !== array() ) {
213 14
			return self::$properties;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return self::$properties; (SMW\DIProperty[]) is incompatible with the return type declared by the abstract method SMW\DataValues\AbstractM...e::getPropertyDataItems of type SMW\DataValues\DIProperty[]|null.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
214
		}
215
216 1
		foreach ( array( '_TEXT', '_LCODE' ) as  $id ) {
217 1
			self::$properties[] = new DIProperty( $id );
218
		}
219
220 1
		return self::$properties;
0 ignored issues
show
Bug Compatibility introduced by
The expression self::$properties; of type null|SMW\DIProperty[] adds the type SMW\DIProperty[] to the return on line 220 which is incompatible with the return type declared by the abstract method SMW\DataValues\AbstractM...e::getPropertyDataItems of type SMW\DataValues\DIProperty[]|null.
Loading history...
221
	}
222
223
	/**
224
	 * @since 2.4
225
	 * @note called by AbstractRecordValue::getDataItems
226
	 *
227
	 * @return DataItem[]
228
	 */
229 2
	public function getDataItems() {
230
231 2
		if ( !$this->isValid() ) {
232
			return array();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array(); (array) is incompatible with the return type declared by the abstract method SMW\DataValues\AbstractMultiValue::getDataItems of type SMW\DataValues\DataItem[]|null.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
233
		}
234
235 2
		$result = array();
236 2
		$index = 0;
237
238 2
		foreach ( $this->getPropertyDataItems() as $diProperty ) {
239 2
			$values = $this->getDataItem()->getSemanticData()->getPropertyValues( $diProperty );
240 2
			if ( count( $values ) > 0 ) {
241 2
				$result[$index] = reset( $values );
242
			} else {
243
				$result[$index] = null;
244
			}
245 2
			$index += 1;
246
		}
247
248 2
		return $result;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $result; (array) is incompatible with the return type declared by the abstract method SMW\DataValues\AbstractMultiValue::getDataItems of type SMW\DataValues\DataItem[]|null.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
249
	}
250
251
	/**
252
	 * @since 2.4
253
	 *
254
	 * @return DataValue|null
255
	 */
256 5
	public function getTextValueByLanguage( $languageCode ) {
257
258 5
		if ( !$this->isValid() || $this->getDataItem() === array() ) {
259
			return null;
260
		}
261
262 5
		$semanticData = $this->getDataItem()->getSemanticData();
263
264 5
		$dataItems = $semanticData->getPropertyValues( new DIProperty( '_LCODE' ) );
265 5
		$dataItem = reset( $dataItems );
266
267 5
		if ( $dataItem === false || ( $dataItem->getString() !== Localizer::asBCP47FormattedLanguageCode( $languageCode ) ) ) {
268 1
			return null;
269
		}
270
271 4
		$dataItems = $semanticData->getPropertyValues( new DIProperty( '_TEXT' ) );
272 4
		$dataItem = reset( $dataItems );
273
274 4
		if ( $dataItem === false ) {
275
			return null;
276
		}
277
278 4
		$dataValue = DataValueFactory::getInstance()->newDataValueByItem(
279
			$dataItem,
280 4
			new DIProperty( '_TEXT' )
281
		);
282
283 4
		return $dataValue;
284
	}
285
286 14
	private function newContainerSemanticData( $value ) {
287
288 14
		if ( $this->m_contextPage === null ) {
289 4
			$containerSemanticData = ContainerSemanticData::makeAnonymousContainer();
290 4
			$containerSemanticData->skipAnonymousCheck();
291
		} else {
292 10
			$subobjectName = '_ML' . md5( $value );
293
294 10
			$subject = new DIWikiPage(
295 10
				$this->m_contextPage->getDBkey(),
296 10
				$this->m_contextPage->getNamespace(),
297 10
				$this->m_contextPage->getInterwiki(),
298
				$subobjectName
299
			);
300
301 10
			$containerSemanticData = new ContainerSemanticData( $subject );
302
		}
303
304 14
		return $containerSemanticData;
305
	}
306
307 17
	private function newLanguageCodeValue( $languageCode ) {
308
309 17
		$languageCodeValue = new LanguageCodeValue();
310
311 17
		if ( $this->m_property !== null ) {
312 10
			$languageCodeValue->setProperty( $this->m_property );
313
		}
314
315 17
		$languageCodeValue->setUserValue( $languageCode );
316
317 17
		return $languageCodeValue;
318
	}
319
320 17
	private function getValueParser() {
321
322 17
		if ( $this->monolingualTextValueParser === null ) {
323 17
			$this->monolingualTextValueParser = ValueParserFactory::getInstance()->newMonolingualTextValueParser();
324
		}
325
326 17
		return $this->monolingualTextValueParser;
327
	}
328
329
}
330