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 SMW\StringCondition; |
||
13 | use SMWContainerSemanticData as ContainerSemanticData; |
||
14 | use SMWDataItem as DataItem; |
||
15 | use SMWDataValue as DataValue; |
||
16 | use SMWDIContainer as DIContainer; |
||
17 | use SMWDIBlob as DIBlob; |
||
18 | |||
19 | /** |
||
20 | * MonolingualTextValue requires two components, a language code and a |
||
21 | * text. |
||
22 | * |
||
23 | * A text `foo@en` is expected to be invoked with a BCP47 language |
||
24 | * code tag and a language dependent text component. |
||
25 | * |
||
26 | * Internally, the value is stored as container object that represents |
||
27 | * the language code and text as separate entities in order to be queried |
||
28 | * individually. |
||
29 | * |
||
30 | * External output representation depends on the context (wiki, html) |
||
31 | * whether the language code is omitted or not. |
||
32 | * |
||
33 | * @license GNU GPL v2+ |
||
34 | * @since 2.4 |
||
35 | * |
||
36 | * @author mwjames |
||
37 | */ |
||
38 | class MonolingualTextValue extends AbstractMultiValue { |
||
39 | |||
40 | /** |
||
41 | * @var DIProperty[]|null |
||
42 | */ |
||
43 | private static $properties = null; |
||
44 | |||
45 | /** |
||
46 | * @var MonolingualTextValueParser |
||
47 | */ |
||
48 | private $monolingualTextValueParser = null; |
||
49 | |||
50 | /** |
||
51 | * @param string $typeid |
||
52 | */ |
||
53 | 29 | public function __construct( $typeid = '' ) { |
|
54 | 29 | parent::__construct( '_mlt_rec' ); |
|
55 | 29 | } |
|
56 | |||
57 | /** |
||
58 | * @see AbstractMultiValue::setFieldProperties |
||
59 | * |
||
60 | * @param DIProperty[] $properties |
||
61 | */ |
||
62 | public function setFieldProperties( array $properties ) { |
||
63 | // Keep the interface while the properties for this type |
||
64 | // are fixed. |
||
65 | } |
||
66 | |||
67 | /** |
||
68 | * @see AbstractMultiValue::getProperties |
||
69 | * |
||
70 | * @param DIProperty[] $properties |
||
71 | */ |
||
72 | public function getProperties() { |
||
73 | self::$properties; |
||
74 | } |
||
75 | |||
76 | /** |
||
77 | * @since 2.5 |
||
78 | * |
||
79 | * @param $userValue |
||
80 | * @param string $languageCode |
||
81 | * |
||
82 | * @return string |
||
83 | */ |
||
84 | 1 | public function getTextWithLanguageTag( $text, $languageCode ) { |
|
85 | 1 | return $text . '@' . Localizer::asBCP47FormattedLanguageCode( $languageCode ); |
|
86 | } |
||
87 | |||
88 | /** |
||
89 | * @see DataValue::parseUserValue |
||
90 | * @note called by DataValue::setUserValue |
||
91 | * |
||
92 | * @param string $userValue |
||
93 | */ |
||
94 | 26 | protected function parseUserValue( $userValue ) { |
|
95 | |||
96 | 26 | list( $text, $languageCode ) = $this->getValuesFromString( $userValue ); |
|
97 | |||
98 | 26 | $languageCodeValue = $this->newLanguageCodeValue( $languageCode ); |
|
99 | |||
100 | if ( |
||
101 | 26 | ( $languageCode !== '' && $languageCodeValue->getErrors() !== array() ) || |
|
102 | 26 | ( $languageCode === '' && $this->isEnabledFeature( SMW_DV_MLTV_LCODE ) ) ) { |
|
103 | 3 | $this->addError( $languageCodeValue->getErrors() ); |
|
104 | 3 | return; |
|
105 | } |
||
106 | |||
107 | 23 | $dataValues = array(); |
|
108 | |||
109 | 23 | foreach ( $this->getPropertyDataItems() as $property ) { |
|
110 | |||
111 | if ( |
||
112 | 23 | ( $languageCode === '' && $property->getKey() === '_LCODE' ) || |
|
113 | 23 | ( $text === '' && $property->getKey() === '_TEXT' ) ) { |
|
114 | 1 | continue; |
|
115 | } |
||
116 | |||
117 | 23 | $value = $text; |
|
118 | |||
119 | 23 | if ( $property->getKey() === '_LCODE' ) { |
|
120 | 22 | $value = $languageCode; |
|
121 | } |
||
122 | |||
123 | 23 | $dataValue = DataValueFactory::getInstance()->newDataValueByProperty( |
|
124 | $property, |
||
125 | $value, |
||
126 | 23 | false, |
|
127 | 23 | $this->m_contextPage |
|
128 | ); |
||
129 | |||
130 | 23 | $this->addError( $dataValue->getErrors() ); |
|
131 | |||
132 | 23 | $dataValues[] = $dataValue; |
|
133 | } |
||
134 | |||
135 | // Generate a hash from the normalized representation so that foo@en being |
||
136 | // the same as foo@EN independent of a user input |
||
137 | 23 | $containerSemanticData = $this->newContainerSemanticData( $text . '@' . $languageCode ); |
|
138 | |||
139 | 23 | foreach ( $dataValues as $dataValue ) { |
|
140 | 23 | $containerSemanticData->addDataValue( $dataValue ); |
|
141 | } |
||
142 | |||
143 | 23 | $this->m_dataitem = new DIContainer( $containerSemanticData ); |
|
144 | |||
145 | // Composite sortkey is to ensure that Store::getPropertyValues can |
||
146 | // apply sorting during value selection |
||
147 | 23 | $this->m_dataitem->addCompositeSortKey( implode( ';', array( $text, $languageCode ) ) ); |
|
148 | 23 | } |
|
149 | |||
150 | /** |
||
151 | * @note called by MonolingualTextValueDescriptionDeserializer::deserialize |
||
152 | * and MonolingualTextValue::parseUserValue |
||
153 | * |
||
154 | * No explicit check is made on the validity of a language code and is |
||
155 | * expected to be done before calling this method. |
||
156 | * |
||
157 | * @since 2.4 |
||
158 | * |
||
159 | * @param string $userValue |
||
160 | * |
||
161 | * @return array |
||
162 | */ |
||
163 | 26 | public function getValuesFromString( $userValue ) { |
|
164 | 26 | return $this->getValueParser()->parse( $userValue ); |
|
165 | } |
||
166 | |||
167 | /** |
||
168 | * @see DataValue::loadDataItem |
||
169 | * |
||
170 | * @param DataItem $dataItem |
||
171 | * |
||
172 | * @return boolean |
||
173 | */ |
||
174 | 14 | protected function loadDataItem( DataItem $dataItem ) { |
|
175 | |||
176 | 14 | if ( $dataItem->getDIType() === DataItem::TYPE_CONTAINER ) { |
|
177 | $this->m_dataitem = $dataItem; |
||
178 | return true; |
||
179 | 14 | } elseif ( $dataItem->getDIType() === DataItem::TYPE_WIKIPAGE ) { |
|
180 | 14 | $semanticData = new ContainerSemanticData( $dataItem ); |
|
181 | 14 | $semanticData->copyDataFrom( ApplicationFactory::getInstance()->getStore()->getSemanticData( $dataItem ) ); |
|
182 | 14 | $this->m_dataitem = new DIContainer( $semanticData ); |
|
183 | 14 | return true; |
|
184 | } |
||
185 | |||
186 | 3 | return false; |
|
187 | } |
||
188 | |||
189 | /** |
||
190 | * @see DataValue::getShortWikiText |
||
191 | */ |
||
192 | 16 | public function getShortWikiText( $linker = null ) { |
|
193 | 16 | return $this->getDataValueFormatter()->format( DataValueFormatter::WIKI_SHORT, $linker ); |
|
194 | } |
||
195 | |||
196 | /** |
||
197 | * @see DataValue::getShortHTMLText |
||
198 | */ |
||
199 | public function getShortHTMLText( $linker = null ) { |
||
200 | return $this->getDataValueFormatter()->format( DataValueFormatter::HTML_SHORT, $linker ); |
||
201 | } |
||
202 | |||
203 | /** |
||
204 | * @see DataValue::getLongWikiText |
||
205 | */ |
||
206 | public function getLongWikiText( $linker = null ) { |
||
207 | return $this->getDataValueFormatter()->format( DataValueFormatter::WIKI_LONG, $linker ); |
||
208 | } |
||
209 | |||
210 | /** |
||
211 | * @see DataValue::getLongHTMLText |
||
212 | */ |
||
213 | public function getLongHTMLText( $linker = null ) { |
||
214 | return $this->getDataValueFormatter()->format( DataValueFormatter::HTML_LONG, $linker ); |
||
215 | } |
||
216 | |||
217 | /** |
||
218 | * @see DataValue::getWikiValue |
||
219 | */ |
||
220 | 5 | public function getWikiValue() { |
|
221 | 5 | return $this->getDataValueFormatter()->format( DataValueFormatter::VALUE ); |
|
222 | } |
||
223 | |||
224 | /** |
||
225 | * @since 2.4 |
||
226 | * @note called by AbstractRecordValue::getPropertyDataItems |
||
227 | * |
||
228 | * @return DIProperty[] |
||
229 | */ |
||
230 | 24 | public function getPropertyDataItems() { |
|
231 | |||
232 | 24 | if ( self::$properties !== null && self::$properties !== array() ) { |
|
233 | 23 | return self::$properties; |
|
0 ignored issues
–
show
|
|||
234 | } |
||
235 | |||
236 | 1 | foreach ( array( '_TEXT', '_LCODE' ) as $id ) { |
|
237 | 1 | self::$properties[] = new DIProperty( $id ); |
|
238 | } |
||
239 | |||
240 | 1 | return self::$properties; |
|
0 ignored issues
–
show
The expression
self::$properties; of type null|SMW\DIProperty[] adds the type SMW\DIProperty[] to the return on line 240 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...
|
|||
241 | } |
||
242 | |||
243 | /** |
||
244 | * @since 2.4 |
||
245 | * |
||
246 | * {@inheritDoc} |
||
247 | */ |
||
248 | 2 | public function getDataItems() { |
|
249 | 2 | return parent::getDataItems(); |
|
250 | } |
||
251 | |||
252 | /** |
||
253 | * @since 2.4 |
||
254 | * |
||
255 | * @param string $languageCode |
||
256 | * |
||
257 | * @return DataValue|null |
||
258 | */ |
||
259 | 10 | public function getTextValueByLanguage( $languageCode ) { |
|
260 | |||
261 | 10 | if ( ( $list = $this->toArray() ) === array() ) { |
|
262 | return null; |
||
263 | } |
||
264 | |||
265 | 10 | if ( $list['_LCODE'] !== Localizer::asBCP47FormattedLanguageCode( $languageCode ) ) { |
|
266 | 3 | return null; |
|
267 | } |
||
268 | |||
269 | 9 | if ( $list['_TEXT'] === '' ) { |
|
270 | return null; |
||
271 | } |
||
272 | |||
273 | 9 | $dataValue = DataValueFactory::getInstance()->newDataValueByItem( |
|
274 | 9 | new DIBlob( $list['_TEXT'] ), |
|
275 | 9 | new DIProperty( '_TEXT' ) |
|
276 | ); |
||
277 | |||
278 | 9 | return $dataValue; |
|
279 | } |
||
280 | |||
281 | /** |
||
282 | * @since 2.5 |
||
283 | * |
||
284 | * @return array |
||
285 | */ |
||
286 | 14 | public function toArray() { |
|
287 | |||
288 | 14 | if ( !$this->isValid() || $this->getDataItem() === array() ) { |
|
289 | return array(); |
||
290 | } |
||
291 | |||
292 | 14 | $semanticData = $this->getDataItem()->getSemanticData(); |
|
293 | |||
294 | $list = array( |
||
295 | 14 | '_TEXT' => '', |
|
296 | '_LCODE' => '' |
||
297 | ); |
||
298 | |||
299 | 14 | $dataItems = $semanticData->getPropertyValues( new DIProperty( '_TEXT' ) ); |
|
300 | 14 | $dataItem = reset( $dataItems ); |
|
301 | |||
302 | 14 | if ( $dataItem !== false ) { |
|
303 | 14 | $list['_TEXT'] = $dataItem->getString(); |
|
304 | } |
||
305 | |||
306 | 14 | $dataItems = $semanticData->getPropertyValues( new DIProperty( '_LCODE' ) ); |
|
307 | 14 | $dataItem = reset( $dataItems ); |
|
308 | |||
309 | 14 | if ( $dataItem !== false ) { |
|
310 | 14 | $list['_LCODE'] = $dataItem->getString(); |
|
311 | } |
||
312 | |||
313 | 14 | return $list; |
|
314 | } |
||
315 | |||
316 | /** |
||
317 | * @since 2.5 |
||
318 | * |
||
319 | * @return string |
||
320 | */ |
||
321 | 1 | public function toString() { |
|
322 | |||
323 | 1 | if ( !$this->isValid() || $this->getDataItem() === array() ) { |
|
324 | return ''; |
||
325 | } |
||
326 | |||
327 | 1 | $list = $this->toArray(); |
|
328 | |||
329 | 1 | return $list['_TEXT'] . '@' . $list['_LCODE']; |
|
330 | } |
||
331 | |||
332 | 23 | private function newContainerSemanticData( $value ) { |
|
333 | |||
334 | 23 | if ( $this->m_contextPage === null ) { |
|
335 | 6 | $containerSemanticData = ContainerSemanticData::makeAnonymousContainer(); |
|
336 | 6 | $containerSemanticData->skipAnonymousCheck(); |
|
337 | } else { |
||
338 | 17 | $subobjectName = '_ML' . md5( $value ); |
|
339 | |||
340 | 17 | $subject = new DIWikiPage( |
|
341 | 17 | $this->m_contextPage->getDBkey(), |
|
342 | 17 | $this->m_contextPage->getNamespace(), |
|
343 | 17 | $this->m_contextPage->getInterwiki(), |
|
344 | $subobjectName |
||
345 | ); |
||
346 | |||
347 | 17 | $containerSemanticData = new ContainerSemanticData( $subject ); |
|
348 | } |
||
349 | |||
350 | 23 | return $containerSemanticData; |
|
351 | } |
||
352 | |||
353 | 26 | private function newLanguageCodeValue( $languageCode ) { |
|
354 | |||
355 | 26 | $languageCodeValue = new LanguageCodeValue(); |
|
356 | |||
357 | 26 | if ( $this->m_property !== null ) { |
|
358 | 17 | $languageCodeValue->setProperty( $this->m_property ); |
|
359 | } |
||
360 | |||
361 | 26 | $languageCodeValue->setUserValue( $languageCode ); |
|
362 | |||
363 | 26 | return $languageCodeValue; |
|
364 | } |
||
365 | |||
366 | 26 | private function getValueParser() { |
|
367 | |||
368 | 26 | if ( $this->monolingualTextValueParser === null ) { |
|
369 | 26 | $this->monolingualTextValueParser = ValueParserFactory::getInstance()->newMonolingualTextValueParser(); |
|
370 | } |
||
371 | |||
372 | 26 | return $this->monolingualTextValueParser; |
|
373 | } |
||
374 | |||
375 | } |
||
376 |
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:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.