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\DataValueFactory; |
||
4 | use SMW\DataValues\AbstractMultiValue; |
||
5 | use SMW\ApplicationFactory; |
||
6 | use SMW\DIProperty; |
||
7 | use SMW\DIWikiPage; |
||
8 | use SMWPropertyListValue as PropertyListValue; |
||
9 | use SMWDataItem as DataItem; |
||
10 | use SMWContainerSemanticData as ContainerSemanticData; |
||
11 | use SMWDIContainer as DIContainer; |
||
12 | |||
13 | /** |
||
14 | * SMWDataValue implements the handling of small sets of property-value pairs. |
||
15 | * The declaration of Records in SMW uses the order of values to encode the |
||
16 | * property that should be used, so the user only needs to enter a list of |
||
17 | * values. Internally, however, the property-value assignments are not stored |
||
18 | * with a particular order; they will only be ordered for display, following |
||
19 | * the declaration. This is why it is not supported to have Records using the |
||
20 | * same property for more than one value. |
||
21 | * |
||
22 | * The class uses DIContainer objects to return its inner state. See the |
||
23 | * documentation for DIContainer for details on how this "pseudo" data |
||
24 | * encapsulated many property assignments. Such data is stored internally |
||
25 | * like a page with various property-value assignments. Indeed, record values |
||
26 | * can be created from DIWikiPage objects (the missing information will |
||
27 | * be fetched from the store). |
||
28 | * |
||
29 | * @todo Enforce limitation of maximal number of values. |
||
30 | * @todo Enforce uniqueness of properties in declaration. |
||
31 | * @todo Complete internationalisation. |
||
32 | * |
||
33 | * @author Markus Krötzsch |
||
34 | * @ingroup SMWDataValues |
||
35 | */ |
||
36 | class SMWRecordValue extends AbstractMultiValue { |
||
37 | |||
38 | /// cache for properties for the fields of this data value |
||
39 | protected $m_diProperties = null; |
||
40 | |||
41 | /** |
||
42 | * @param string $typeid |
||
43 | */ |
||
44 | 31 | public function __construct( $typeid = '' ) { |
|
45 | 31 | parent::__construct( '_rec' ); |
|
46 | 31 | } |
|
47 | |||
48 | /** |
||
49 | * @since 2.3 |
||
50 | * |
||
51 | * @return DIProperty[]|null |
||
52 | */ |
||
53 | 1 | public function getProperties() { |
|
54 | 1 | return $this->m_diProperties; |
|
0 ignored issues
–
show
Bug
Compatibility
introduced
by
Loading history...
|
|||
55 | } |
||
56 | |||
57 | /** |
||
58 | * @since 2.3 |
||
59 | * |
||
60 | * @param string $value |
||
61 | * |
||
62 | * @return array |
||
63 | */ |
||
64 | 28 | public function getValuesFromString( $value ) { |
|
65 | // #664 / T17732 |
||
66 | 28 | $value = str_replace( "\;", "-3B", $value ); |
|
67 | |||
68 | // Bug 21926 / T23926 |
||
69 | // Values that use html entities are encoded with a semicolon |
||
70 | 28 | $value = htmlspecialchars_decode( $value, ENT_QUOTES ); |
|
71 | 28 | $values = preg_split( '/[\s]*;[\s]*/u', trim( $value ) ); |
|
72 | |||
73 | 28 | return str_replace( "-3B", ";", $values ); |
|
74 | } |
||
75 | |||
76 | 24 | protected function parseUserValue( $value ) { |
|
77 | |||
78 | 24 | if ( $value === '' ) { |
|
79 | 1 | $this->addErrorMsg( array( 'smw_novalues' ) ); |
|
80 | 1 | return; |
|
81 | } |
||
82 | |||
83 | 23 | $containerSemanticData = $this->newContainerSemanticData( $value ); |
|
84 | 23 | $sortKeys = array(); |
|
85 | |||
86 | 23 | $values = $this->getValuesFromString( $value ); |
|
87 | 23 | $valueIndex = 0; // index in value array |
|
88 | 23 | $propertyIndex = 0; // index in property list |
|
89 | 23 | $empty = true; |
|
90 | |||
91 | 23 | foreach ( $this->getPropertyDataItems() as $diProperty ) { |
|
92 | |||
93 | 23 | if ( !array_key_exists( $valueIndex, $values ) || $this->getErrors() !== array() ) { |
|
94 | 1 | break; // stop if there are no values left |
|
95 | } |
||
96 | |||
97 | // generating the DVs: |
||
98 | 23 | if ( ( $values[$valueIndex] === '' ) || ( $values[$valueIndex] == '?' ) ) { // explicit omission |
|
99 | 1 | $valueIndex++; |
|
100 | } else { |
||
101 | 23 | $dataValue = DataValueFactory::getInstance()->newDataValueByProperty( |
|
102 | $diProperty, |
||
103 | 23 | $values[$valueIndex], |
|
104 | 23 | false, |
|
105 | 23 | $this->getContextPage() |
|
106 | ); |
||
107 | |||
108 | 23 | if ( $dataValue->isValid() ) { // valid DV: keep |
|
109 | 23 | $containerSemanticData->addPropertyObjectValue( $diProperty, $dataValue->getDataItem() ); |
|
110 | 23 | $sortKeys[] = $dataValue->getDataItem()->getSortKey(); |
|
111 | |||
112 | 23 | $valueIndex++; |
|
113 | 23 | $empty = false; |
|
114 | 1 | } elseif ( ( count( $values ) - $valueIndex ) == ( count( $this->m_diProperties ) - $propertyIndex ) ) { |
|
115 | 1 | $containerSemanticData->addPropertyObjectValue( $diProperty, $dataValue->getDataItem() ); |
|
116 | 1 | $this->addError( $dataValue->getErrors() ); |
|
117 | 1 | ++$valueIndex; |
|
118 | } |
||
119 | } |
||
120 | 23 | ++$propertyIndex; |
|
121 | } |
||
122 | |||
123 | 23 | if ( $empty && $this->getErrors() === array() ) { |
|
124 | $this->addErrorMsg( array( 'smw_novalues' ) ); |
||
125 | } |
||
126 | |||
127 | 23 | $this->m_dataitem = new DIContainer( $containerSemanticData ); |
|
128 | |||
129 | // Composite sortkey is to ensure that Store::getPropertyValues can |
||
130 | // apply sorting during value selection |
||
131 | 23 | $this->m_dataitem->addCompositeSortKey( implode( ';', $sortKeys ) ); |
|
132 | 23 | } |
|
133 | |||
134 | /** |
||
135 | * @see SMWDataValue::loadDataItem() |
||
136 | * @param $dataitem DataItem |
||
137 | * @return boolean |
||
138 | */ |
||
139 | 7 | protected function loadDataItem( DataItem $dataItem ) { |
|
140 | 7 | if ( $dataItem->getDIType() == DataItem::TYPE_CONTAINER ) { |
|
141 | $this->m_dataitem = $dataItem; |
||
142 | return true; |
||
143 | 7 | } elseif ( $dataItem->getDIType() == DataItem::TYPE_WIKIPAGE ) { |
|
144 | 7 | $semanticData = new ContainerSemanticData( $dataItem ); |
|
145 | 7 | $semanticData->copyDataFrom( ApplicationFactory::getInstance()->getStore()->getSemanticData( $dataItem ) ); |
|
146 | 7 | $this->m_dataitem = new DIContainer( $semanticData ); |
|
147 | 7 | return true; |
|
148 | } else { |
||
149 | 2 | return false; |
|
150 | } |
||
151 | } |
||
152 | |||
153 | 8 | public function getShortWikiText( $linked = null ) { |
|
154 | 8 | if ( $this->m_caption !== false ) { |
|
155 | return $this->m_caption; |
||
156 | } |
||
157 | 8 | return $this->makeOutputText( 0, $linked ); |
|
158 | } |
||
159 | |||
160 | public function getShortHTMLText( $linker = null ) { |
||
161 | if ( $this->m_caption !== false ) { |
||
162 | return $this->m_caption; |
||
163 | } |
||
164 | return $this->makeOutputText( 1, $linker ); |
||
165 | } |
||
166 | |||
167 | public function getLongWikiText( $linked = null ) { |
||
168 | return $this->makeOutputText( 2, $linked ); |
||
169 | } |
||
170 | |||
171 | public function getLongHTMLText( $linker = null ) { |
||
172 | return $this->makeOutputText( 3, $linker ); |
||
173 | } |
||
174 | |||
175 | 9 | public function getWikiValue() { |
|
176 | 9 | return $this->makeOutputText( 4 ); |
|
177 | } |
||
178 | |||
179 | /// @todo Allowed values for multi-valued properties are not supported yet. |
||
180 | 22 | protected function checkAllowedValues() { |
|
181 | 22 | } |
|
182 | |||
183 | /** |
||
184 | * Make sure that the content is reset in this case. |
||
185 | * @todo This is not a full reset yet (the case that property is changed after a value |
||
186 | * was set does not occur in the normal flow of things, hence this has low priority). |
||
187 | */ |
||
188 | 21 | public function setProperty( DIProperty $property ) { |
|
189 | 21 | parent::setProperty( $property ); |
|
190 | 21 | $this->m_diProperties = null; |
|
191 | 21 | } |
|
192 | |||
193 | /** |
||
194 | * @since 2.1 |
||
195 | * |
||
196 | * @param DIProperty[] $properties |
||
197 | */ |
||
198 | 8 | public function setFieldProperties( array $properties ) { |
|
199 | 8 | foreach ( $properties as $property ) { |
|
200 | 8 | if ( $property instanceof DIProperty ) { |
|
201 | 8 | $this->m_diProperties[] = $property; |
|
202 | } |
||
203 | } |
||
204 | 8 | } |
|
205 | |||
206 | ////// Additional API for value lists |
||
207 | |||
208 | /** |
||
209 | * @deprecated as of 1.6, use getDataItems instead |
||
210 | * |
||
211 | * @return array of DataItem |
||
212 | */ |
||
213 | public function getDVs() { |
||
214 | return $this->getDataItems(); |
||
215 | } |
||
216 | |||
217 | /** |
||
218 | * @since 1.6 |
||
219 | * |
||
220 | * {@inheritDoc} |
||
221 | */ |
||
222 | 8 | public function getDataItems() { |
|
223 | 8 | return parent::getDataItems(); |
|
224 | } |
||
225 | |||
226 | /** |
||
227 | * Return the array (list) of properties that the individual entries of |
||
228 | * this datatype consist of. |
||
229 | * |
||
230 | * @since 1.6 |
||
231 | * |
||
232 | * @todo I18N for error message. |
||
233 | * |
||
234 | * @return array of DIProperty |
||
235 | */ |
||
236 | 28 | public function getPropertyDataItems() { |
|
237 | 28 | if ( is_null( $this->m_diProperties ) ) { |
|
238 | 20 | $this->m_diProperties = $this->findPropertyDataItems( $this->m_property ); |
|
239 | |||
240 | 20 | if ( count( $this->m_diProperties ) == 0 ) { // TODO internalionalize |
|
241 | 6 | $this->addError( 'The list of properties to be used for the data fields has not been specified properly.' ); |
|
242 | } |
||
243 | } |
||
244 | |||
245 | 28 | return $this->m_diProperties; |
|
0 ignored issues
–
show
The return type of
return $this->m_diProperties; (array ) 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
Loading history...
|
|||
246 | } |
||
247 | |||
248 | /** |
||
249 | * Return the array (list) of properties that the individual entries of |
||
250 | * this datatype consist of. |
||
251 | * |
||
252 | * @since 1.6 |
||
253 | * |
||
254 | * @param $diProperty mixed null or DIProperty object for which to retrieve the types |
||
255 | * |
||
256 | * @return array of DIProperty |
||
257 | */ |
||
258 | 20 | protected function findPropertyDataItems( $diProperty ) { |
|
259 | 20 | if ( !is_null( $diProperty ) ) { |
|
260 | 20 | $propertyDiWikiPage = $diProperty->getDiWikiPage(); |
|
261 | |||
262 | 20 | if ( !is_null( $propertyDiWikiPage ) ) { |
|
263 | 20 | $listDiProperty = new DIProperty( '_LIST' ); |
|
264 | 20 | $dataItems = ApplicationFactory::getInstance()->getStore()->getPropertyValues( $propertyDiWikiPage, $listDiProperty ); |
|
265 | |||
266 | 20 | if ( count( $dataItems ) == 1 ) { |
|
267 | 20 | $propertyListValue = new PropertyListValue( '__pls' ); |
|
268 | 20 | $propertyListValue->setDataItem( reset( $dataItems ) ); |
|
269 | |||
270 | 20 | if ( $propertyListValue->isValid() ) { |
|
271 | 20 | return $propertyListValue->getPropertyDataItems(); |
|
272 | } |
||
273 | } |
||
274 | } |
||
275 | } |
||
276 | |||
277 | 6 | return array(); |
|
278 | } |
||
279 | |||
280 | ////// Internal helper functions |
||
281 | |||
282 | 13 | protected function makeOutputText( $type = 0, $linker = null ) { |
|
283 | 13 | if ( !$this->isValid() ) { |
|
284 | return ( ( $type == 0 ) || ( $type == 1 ) ) ? '' : $this->getErrorText(); |
||
285 | } |
||
286 | |||
287 | 13 | $result = ''; |
|
288 | 13 | $i = 0; |
|
289 | 13 | foreach ( $this->getPropertyDataItems() as $propertyDataItem ) { |
|
290 | 13 | if ( $i == 1 ) { |
|
291 | 13 | $result .= ( $type == 4 ) ? '; ' : ' ('; |
|
292 | 13 | } elseif ( $i > 1 ) { |
|
293 | 1 | $result .= ( $type == 4 ) ? '; ' : ', '; |
|
294 | } |
||
295 | 13 | ++$i; |
|
296 | 13 | $propertyValues = $this->m_dataitem->getSemanticData()->getPropertyValues( $propertyDataItem ); // combining this with next line violates PHP strict standards |
|
297 | 13 | $dataItem = reset( $propertyValues ); |
|
298 | 13 | if ( $dataItem !== false ) { |
|
299 | 13 | $dataValue = DataValueFactory::getInstance()->newDataValueByItem( $dataItem, $propertyDataItem ); |
|
300 | 13 | $result .= $this->makeValueOutputText( $type, $dataValue, $linker ); |
|
301 | } else { |
||
302 | 13 | $result .= '?'; |
|
303 | } |
||
304 | } |
||
305 | 13 | if ( ( $i > 1 ) && ( $type != 4 ) ) { |
|
306 | 8 | $result .= ')'; |
|
307 | } |
||
308 | |||
309 | 13 | return $result; |
|
310 | } |
||
311 | |||
312 | 13 | protected function makeValueOutputText( $type, $dataValue, $linker ) { |
|
313 | switch ( $type ) { |
||
314 | 13 | case 0: |
|
315 | 8 | return $dataValue->getShortWikiText( $linker ); |
|
316 | 9 | case 1: |
|
317 | return $dataValue->getShortHTMLText( $linker ); |
||
318 | 9 | case 2: |
|
319 | return $dataValue->getShortWikiText( $linker ); |
||
320 | 9 | case 3: |
|
321 | return $dataValue->getShortHTMLText( $linker ); |
||
322 | 9 | case 4: |
|
323 | 9 | return str_replace( ";", "\;", $dataValue->getWikiValue() ); |
|
324 | } |
||
325 | } |
||
326 | |||
327 | 23 | private function newContainerSemanticData( $value ) { |
|
328 | |||
329 | 23 | if ( $this->m_contextPage === null ) { |
|
330 | 9 | $containerSemanticData = ContainerSemanticData::makeAnonymousContainer(); |
|
331 | 9 | $containerSemanticData->skipAnonymousCheck(); |
|
332 | } else { |
||
333 | 17 | $subobjectName = '_' . hash( 'md4', $value, false ); // md4 is probably fastest of PHP's hashes |
|
334 | |||
335 | 17 | $subject = new DIWikiPage( |
|
336 | 17 | $this->m_contextPage->getDBkey(), |
|
337 | 17 | $this->m_contextPage->getNamespace(), |
|
338 | 17 | $this->m_contextPage->getInterwiki(), |
|
339 | $subobjectName |
||
340 | ); |
||
341 | |||
342 | 17 | $containerSemanticData = new ContainerSemanticData( $subject ); |
|
343 | } |
||
344 | |||
345 | 23 | return $containerSemanticData; |
|
346 | } |
||
347 | |||
348 | } |
||
349 |