Completed
Push — master ( 9b2ed5...981814 )
by Jeroen De
76:07
created

formats/array/SRF_Array.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
 * Query format for arrays with features for Extensions 'Arrays' and 'HashTables'
4
 *
5
 * @file
6
 * @ingroup SemanticResultFormats
7
 * @author Daniel Werner < [email protected] >
8
 *
9
 * Doesn't require 'Arrays' nor 'HashTables' exytensions but has additional features
10
 * ('name' parameter in either result format) if they are available.
11
 *
12
 * Arrays 2.0+ and HashTables 1.0+ are recommended but not necessary.
13
 */
14
15
/**
16
 * Array format
17
 */
18
class SRFArray extends SMWResultPrinter {
19
20
	protected static $mDefaultSeps = [];
21
	protected $mSep;
22
	protected $mPropSep;
23
	protected $mManySep;
24
	protected $mRecordSep;
25
	protected $mHeaderSep;
26
	protected $mArrayName = null;
27
	protected $mShowPageTitles;
28
29
	protected $mHideRecordGaps;
30
	protected $mHidePropertyGaps;
31
32
	/**
33
	 * @var Boolean true if 'mainlabel' parameter is set to '-'
34
	 */
35
	protected $mMainLabelHack = false;
36
37 2
	public function __construct( $format, $inline = true ) {
38 2
		parent::__construct( $format, $inline );
39
		//overwrite parent default behavior for linking:
40 2
		$this->mLinkFirst = false;
41 2
		$this->mLinkOthers = false;
42 2
	}
43
44
	public function getQueryMode( $context ) {
45
		return SMWQuery::MODE_INSTANCES;
46
	}
47
48
	public function getName() {
49
		// Give grep a chance to find the usages:
50
		// srf_printername_array, srf_printername_hash
51
		return wfMessage( 'srf_printername_' . $this->mFormat )->text();
52
	}
53
54
	/*
55
	// By overwriting this function, we disable default searchlabel handling?
56
	public function getResult( SMWQueryResult $results, array $params, $outputmode ) {
57
		$this->handleParameters( $params, $outputmode );
58
		return $this->getResultText( $results, $outputmode );
59
	}
60
	*/
61
62
	protected function getResultText( SMWQueryResult $res, $outputmode ) {
63
		/*
64
		 * @todo
65
		 * labels of requested properties could define default values. Seems not possible at the moment because
66
		 * SMWPrintRequest::getLable() always returns the property name even if no specific label is defined.
67
		 */
68
69
		$perPage_items = [];
70
71
		//for each page:
72
		while ( $row = $res->getNext() ) {
73
			$perProperty_items = [];
74
75
			/**
76
			 * first field is always the page title, except, mainlabel is set to '-'
77
			 *
78
			 * @todo Is there some other way to check the data value directly for being the
79
			 *        page title or not? SMWs behavior could change on mainlabel handling...
80
			 */
81
			$isPageTitle = !$this->mMainLabelHack;
82
83
			//for each property on that page:
84
			foreach ( $row as $field ) { // $row is array(), $field of type SMWResultArray
85
				$manyValue_items = [];
86
				$isMissingProperty = false;
87
88
				$manyValues = $field->getContent();
89
90
				//If property is not set (has no value) on a page:
91
				if ( empty( $manyValues ) ) {
92
					$delivery = $this->deliverMissingProperty( $field );
93
					$manyValue_items = $this->fillDeliveryArray( $manyValue_items, $delivery );
94
					$isMissingProperty = true;
95
				} else //otherwise collect property value (potentially many values):
96
				{
97
					while ( $obj = $field->getNextDataValue() ) {
98
99
						$value_items = [];
100
						$isRecord = false;
101
102
						// handle page Title:
103
						if ( $isPageTitle ) {
104
							if ( !$this->mShowPageTitles ) {
105
								$isPageTitle = false;
106
								continue 2; //next property
107
							}
108
							$value_items = $this->fillDeliveryArray(
109
								$value_items,
110
								$this->deliverPageTitle( $obj, $this->mLinkFirst )
111
							);
112
						} // handle record values:
113
						elseif ( $obj instanceof SMWRecordValue ) {
114
							$recordItems = $obj->getDataItems();
115
							// walk all single values of the record set:
116
							foreach ( $recordItems as $dataItem ) {
117
								$recordField = $dataItem !== null ? SMWDataValueFactory::getInstance(
118
								)->newDataValueByItem( $dataItem, null ) : null;
119
								$value_items = $this->fillDeliveryArray(
120
									$value_items,
121
									$this->deliverRecordField( $recordField, $this->mLinkOthers )
122
								);
123
							}
124
							$isRecord = true;
125
						} // handle normal data values:
126
						else {
127
							$value_items = $this->fillDeliveryArray(
128
								$value_items,
129
								$this->deliverSingleValue( $obj, $this->mLinkOthers )
130
							);
131
						}
132
						$delivery = $this->deliverSingleManyValuesData( $value_items, $isRecord, $isPageTitle );
133
						$manyValue_items = $this->fillDeliveryArray( $manyValue_items, $delivery );
134
					}
135
				} // foreach...
136
				$delivery = $this->deliverPropertiesManyValues(
137
					$manyValue_items,
138
					$isMissingProperty,
139
					$isPageTitle,
140
					$field
141
				);
142
				$perProperty_items = $this->fillDeliveryArray( $perProperty_items, $delivery );
143
				$isPageTitle = false; // next one could be record or normal value
144
			} // foreach...			
145
			$delivery = $this->deliverPageProperties( $perProperty_items );
146
			$perPage_items = $this->fillDeliveryArray( $perPage_items, $delivery );
147
		} // while...
148
149
		$output = $this->deliverQueryResultPages( $perPage_items );
150
151
		return $output;
152
	}
153
154
	protected function fillDeliveryArray( $array = [], $value = null ) {
155
		if ( !is_null( $value ) ) { //don't create any empty entries
156
			$array[] = $value;
157
		}
158
		return $array;
159
	}
160
161
	protected function deliverPageTitle( $value, $link = false ) {
162
		return $this->deliverSingleValue( $value, $link );
163
	}
164
165
	protected function deliverRecordField( $value, $link = false ) {
166
		if ( $value !== null ) // contains value
167
		{
168
			return $this->deliverSingleValue( $value, $link );
169
		} elseif ( $this->mHideRecordGaps ) {
170
			return null;
171
		} // hide gap
172
		else {
173
			return '';
174
		} // empty string will make sure that record value separators are generated
175
	}
176
177
	protected function deliverSingleValue( $value, $link = false ) {
178
		//return trim( $value->getShortWikiText( $link ) );
179
		return trim(
180
			Sanitizer::decodeCharReferences( $value->getShortWikiText( $link ) )
181
		); // decode: better for further processing with array extension
182
	}
183
184
	// Property not declared on a page:
185
	protected function deliverMissingProperty( SMWResultArray $field ) {
186
		if ( $this->mHidePropertyGaps ) {
187
			return null;
188
		} else {
189
			return '';
190
		} //empty string will make sure that array separator will be generated
191
		/** @ToDo: System for Default values?... * */
192
	}
193
194
	//represented by an array of record fields or just a single array value:
195
	protected function deliverSingleManyValuesData( $value_items, $containsRecord, $isPageTitle ) {
196
		if ( empty( $value_items ) ) //happens when one of the higher functions delivers null
197
		{
198
			return null;
199
		}
200
		return implode( $this->mRecordSep, $value_items );
201
	}
202
203
	protected function deliverPropertiesManyValues( $manyValue_items, $isMissingProperty, $isPageTitle, SMWResultArray $data ) {
204
		if ( empty( $manyValue_items ) ) {
205
			return null;
206
		}
207
208
		$text = implode( $this->mManySep, $manyValue_items );
209
210
		// if property names should be displayed and this is not the page titles value:
211
		if ( $this->mShowHeaders != SMW_HEADERS_HIDE && !$isPageTitle ) {
212
			$linker = $this->mShowHeaders == SMW_HEADERS_PLAIN ? null : $this->mLinker;
213
			$text = $data->getPrintRequest()->getText( SMW_OUTPUT_WIKI, $linker ) . $this->mHeaderSep . $text;
214
		}
215
		return $text;
216
	}
217
218
	protected function deliverPageProperties( $perProperty_items ) {
219
		if ( empty( $perProperty_items ) ) {
220
			return null;
221
		}
222
		return implode( $this->mPropSep, $perProperty_items );
223
	}
224
225
	protected function deliverQueryResultPages( $perPage_items ) {
226
		if ( $this->mArrayName !== null ) {
227
			$this->createArray( $perPage_items ); //create Array
228
			return '';
229
		} else {
230
			return implode( $this->mSep, $perPage_items );
231
		}
232
	}
233
234
	/**
235
	 * Helper function to create a new Array within 'Arrays' extension. Takes care of different versions
236
	 * as well as the old 'ArrayExtension'.
237
	 */
238
	protected function createArray( $array ) {
239
		global $wgArrayExtension;
240
241
		$arrayId = $this->mArrayName;
242
243
		if ( defined( 'ExtArrays::VERSION' ) ) {
244
			// 'Arrays' extension 2+
245
			global $wgParser;
246
			/** ToDo: is there a way to get the actual parser which has started the query? */
247
			ExtArrays::get( $wgParser )->createArray( $arrayId, $array );
248
			return true;
249
		}
250
251
		// compatbility to 'ArrayExtension' extension before 2.0:
252
253
		if ( !isset( $wgArrayExtension ) ) {
254
			//Hash extension is not installed in this wiki
255
			return false;
256
		}
257
		$version = null;
258
		if ( defined( 'ArrayExtension::VERSION' ) ) {
259
			$version = ArrayExtension::VERSION;
260
		} elseif ( defined( 'ExtArrayExtension::VERSION' ) ) {
261
			$version = ExtArrayExtension::VERSION;
262
		}
263
		if ( $version !== null && version_compare( $version, '1.3.2', '>=' ) ) {
264
			// ArrayExtension 1.3.2+
265
			$wgArrayExtension->createArray( $arrayId, $array );
266
		} else {
267
			// dirty way
268
			$wgArrayExtension->mArrays[trim( $arrayId )] = $array;
269
		}
270
		return true;
271
	}
272
273
	protected function initializeCfgValue( $dfltVal, $dfltCacheKey ) {
274
		$cache = &self::$mDefaultSeps[$dfltCacheKey];
275
		if ( !isset( $cache ) ) {
276
			$cache = $this->getCfgSepText( $dfltVal );
277
			if ( $cache === null ) {
278
				// cache can't be initialized, propably function-reference in userconfig
279
				// but format is not used in inline context, use fallback in this case:
280
				global $srfgArraySepTextualFallbacks;
281
				$cache = $srfgArraySepTextualFallbacks[$dfltCacheKey];
282
			}
283
		}
284
		return $cache;
285
	}
286
287
	protected function getCfgSepText( $obj ) {
288
		if ( is_array( $obj ) ) {
289
			// invalid definition:
290
			if ( !array_key_exists( 0, $obj ) ) {
291
				return null;
292
			}
293
294
			// check for config-defined arguments to pass to the page before processing it:			
295
			if ( array_key_exists( 'args', $obj ) && is_array( $obj['args'] ) ) {
296
				$params = $obj['args'];
297
			} else {
298
				$params = [];
299
			} // no arguments
300
301
			// create title of page whose text should be used as separator:
302
			$obj = Title::newFromText( $obj[0], ( array_key_exists( 1, $obj ) ? $obj[1] : NS_MAIN ) );
303
		}
304
		if ( $obj instanceof Title ) {
305
			$article = new Article( $obj );
306
		} elseif ( $obj instanceof Article ) {
307
			$article = $obj;
308
		} else {
309
			return $obj; //only text
310
		}
311
312
		global $wgParser;
313
		/*
314
		 * Feature to use page value as separator only works if Parser::parse() is running!
315
		 * That's not the case on semantic search special page for example!
316
		 */
317
		// can't use $this->mInline here since SMW 1.6.2 had a bug setting it to false in most cases!		
318
		if ( !isset( $wgParser->mOptions ) ) {
319
			//if( ! $this->mInline ) {
320
			return null;
321
		}
322
323
		/*
324
		 * parse page as if it were included like a template. Never use Parser::recursiveTagParse() or similar 
325
		 * for this since it would call hooks we don't want to call and won't return wiki text for inclusion!
326
		 */
327
		$frame = $wgParser->getPreprocessor()->newCustomFrame( $params );
328
		// compatibility for 1.19, getContent() was implemented in 1.21.
329
		// FIXME: Remove when support for MediaWiki 1.19 is dropped
330
		if ( method_exists( $article, 'getContent' ) ) {
331
			$content = $article->getContent( Revision::RAW )->getNativeData();
332
		} else {
333
			$content = $article->getRawText();
334
		}
335
		$text = $wgParser->preprocessToDom( $content, Parser::PTD_FOR_INCLUSION );
336
		$text = trim( $frame->expand( $text ) );
337
338
		return $text;
339
	}
340
341
	protected function handleParameters( array $params, $outputmode ) {
342
		// does the link parameter:
343
		parent::handleParameters( $params, $outputmode );
344
345
		//separators:
346
		$this->mSep = $params['sep'];
347
		$this->mPropSep = $params['propsep'];
348
		$this->mManySep = $params['manysep'];
349
		$this->mRecordSep = $params['recordsep'];
350
		$this->mHeaderSep = $params['headersep'];
351
352
		// only use this in inline mode, if text is given. Since SMW 1.6.2 '' is given, so if
353
		// we wouldn't check, we would always end up with an array instead of visible output
354
		if ( $params['name'] !== false && ( $this->mInline || trim( $params['name'] ) !== '' ) ) {
355
			$this->mArrayName = trim( $params['name'] );
356
			$this->createArray(
357
				[]
358
			); //create empty array in case we get no result so we won't have an undefined array in the end.
359
		}
360
361
		// if mainlabel set to '-', this will cause the titles not to appear, so make sure we catch this!
362
		$this->mMainLabelHack = trim( $params['mainlabel'] ) === '-';
363
364
		// whether or not to display the page title:
365
		$this->mShowPageTitles = strtolower( $params['titles'] ) != 'hide';
366
367
		switch ( strtolower( $params['hidegaps'] ) ) {
368
			case 'none':
369
				$this->mHideRecordGaps = false;
370
				$this->mHidePropertyGaps = false;
371
				break;
372
			case 'all':
373
				$this->mHideRecordGaps = true;
374
				$this->mHidePropertyGaps = true;
375
				break;
376
			case 'property':
377
			case 'prop':
378
			case 'attribute':
379
			case 'attr':
380
				$this->mHideRecordGaps = false;
381
				$this->mHidePropertyGaps = true;
382
				break;
383
			case 'record':
384
			case 'rec':
385
			case 'rcrd':
386
			case 'n-ary':
387
			case 'nary':
388
				$this->mHideRecordGaps = true;
389
				$this->mHidePropertyGaps = false;
390
				break;
391
		}
392
	}
393
394
	/**
395
	 * @see SMWResultPrinter::getParamDefinitions
396
	 *
397
	 * @since 1.8
398
	 *
399
	 * @param $definitions array of IParamDefinition
400
	 *
401
	 * @return array of IParamDefinition|array
402
	 */
403
	public function getParamDefinitions( array $definitions ) {
0 ignored issues
show
getParamDefinitions uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
404
		$params = parent::getParamDefinitions( $definitions );
405
406
		### adjusted basic SMW params: ###
407
408
		$definitions['limit']->setDefault( $GLOBALS['smwgQMaxInlineLimit'] );
409
		$definitions['link']->setDefault( 'none' );
410
		$definitions['headers']->setDefault( 'hide' );
411
412
		### new params: ###
413
414
		$params['titles'] = [
415
			'message' => 'srf_paramdesc_pagetitle',
416
			'values' => [ 'show', 'hide' ],
417
			'aliases' => [ 'pagetitle', 'pagetitles' ],
418
			'default' => 'show',
419
		];
420
421
		$params['hidegaps'] = [
422
			'message' => 'srf_paramdesc_hidegaps',
423
			'values' => [ 'none', 'all', 'property', 'record' ],
424
			'default' => 'none',
425
		];
426
427
		$params['name'] = [
428
			'message' => 'srf_paramdesc_arrayname',
429
			'default' => false,
430
			'manipulatedefault' => false,
431
		];
432
433
		// separators (default values are defined in the following globals:)
434
		global $srfgArraySep, $srfgArrayPropSep, $srfgArrayManySep, $srfgArrayRecordSep, $srfgArrayHeaderSep;
435
436
		$params['sep'] = [
437
			'message' => 'smw-paramdesc-sep',
438
			'default' => $this->initializeCfgValue( $srfgArraySep, 'sep' ),
439
		];
440
441
		$params['propsep'] = [
442
			'message' => 'srf_paramdesc_propsep',
443
			'default' => $this->initializeCfgValue( $srfgArrayPropSep, 'propsep' ),
444
		];
445
446
		$params['manysep'] = [
447
			'message' => 'srf_paramdesc_manysep',
448
			'default' => $this->initializeCfgValue( $srfgArrayManySep, 'manysep' ),
449
		];
450
451
		$params['recordsep'] = [
452
			'message' => 'srf_paramdesc_recordsep',
453
			'default' => $this->initializeCfgValue( $srfgArrayRecordSep, 'recordsep' ),
454
			'aliases' => [ 'narysep', 'rcrdsep', 'recsep' ],
455
		];
456
457
		$params['headersep'] = [
458
			'message' => 'srf_paramdesc_headersep',
459
			'default' => $this->initializeCfgValue( $srfgArrayHeaderSep, 'headersep' ),
460
			'aliases' => [ 'narysep', 'rcrdsep', 'recsep' ],
461
		];
462
463
		return $params;
464
	}
465
466
}
467
468
469
470