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
|
|||
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
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
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
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
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
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 |
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.