Completed
Push — master ( 14d2bd...06e609 )
by mw
81:37 queued 59:24
created

includes/export/SMW_ExportController.php (3 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
use SMW\ApplicationFactory;
4
use SMW\DIProperty;
5
use SMW\DIWikiPage;
6
use SMW\Query\PrintRequest;
7
use SMW\SemanticData;
8
9
/**
10
 * File holding the SMWExportController class that provides basic functions for
11
 * exporting pages to RDF and OWL.
12
 *
13
 * @ingroup SMW
14
 *
15
 * @author Markus Krötzsch
16
 */
17
18
/**
19
 * Class for controlling the export of SMW page data, supporting high-level
20
 * features such as recursive export and backlink inclusion. The class controls
21
 * export independent of the serialisation syntax that is used.
22
 *
23
 * @ingroup SMW
24
 */
25
class SMWExportController {
26
	const MAX_CACHE_SIZE = 5000; // do not let cache arrays get larger than this
27
	const CACHE_BACKJUMP = 500;  // kill this many cached entries if limit is reached,
28
	                             // avoids too much array copying; <= MAX_CACHE_SIZE!
29
	/**
30
	 * The object used for serialisation.
31
	 * @var SMWSerializer
32
	 */
33
	protected $serializer;
34
	/**
35
	 * An array that keeps track of the elements for which we still need to
36
	 * write auxiliary definitions/declarations.
37
	 */
38
	protected $element_queue;
39
	/**
40
	 * An array that keeps track of the recursion depth with which each object
41
	 * has been serialised.
42
	 */
43
	protected $element_done;
44
	/**
45
	 * Boolean to indicate whether all objects that are exported in full (with
46
	 * all data) should also lead to the inclusion of all "inlinks" that they
47
	 * receive from other objects. If yes, these other objects are also
48
	 * serialised with at least the relevant inlinking properties included.
49
	 * Adding such dependencies counts as "recursive serialisation" and whether
50
	 * or not inlinking objects are included in full depends on the setting for
51
	 * recursion depth. Setting this to true enables "browsable RDF".
52
	 */
53
	protected $add_backlinks;
54
	/**
55
	 * Controls how long to wait until flushing content to output. Flushing
56
	 * early may reduce the memory footprint of serialization functions.
57
	 * Flushing later has some advantages for export formats like RDF/XML where
58
	 * global namespace declarations are only possible by modifying the header,
59
	 * so that only local declarations are possible after the first flush.
60
	 */
61
	protected $delay_flush;
62
	/**
63
	 * File handle for a potential output file to write to, or null if printing
64
	 * to standard output.
65
	 */
66
	protected $outputfile;
67
68
	/**
69
	 * @var DeepRedirectTargetResolver
70
	 */
71
	private $deepRedirectTargetResolver = null;
72
73
	/**
74
	 * Constructor.
75
	 * @param SMWSerializer $serializer defining the object used for syntactic
76
	 * serialization.
77
	 * @param boolean $enable_backlinks defining if backlinks are included,
78
	 * see $add_backlinks for details.
79
	 */
80 12
	public function __construct( SMWSerializer $serializer, $enable_backlinks = false ) {
81 12
		$this->serializer = $serializer;
82 12
		$this->outputfile = null;
83 12
		$this->add_backlinks = $enable_backlinks;
84 12
	}
85
86
	/**
87
	 * Enable or disable inclusion of backlinks into the output.
88
	 * @param boolean $enable
89
	 */
90 10
	public function enableBacklinks( $enable ) {
91 10
		$this->add_backlinks = $enable;
92 10
	}
93
94
	/**
95
	 * Initialize all internal structures to begin with some serialization.
96
	 * Returns true if initialization was successful (this means that the
97
	 * optional output file is writable).
98
	 * @param string $outfilename URL of the file that output should be written
99
	 * to, or empty string for writting to the standard output.
100
	 *
101
	 * @return boolean
102
	 */
103 12
	protected function prepareSerialization( $outfilename = '' ) {
104 12
		$this->serializer->clear();
105 12
		$this->element_queue = array();
106 12
		$this->element_done = array();
107 12
		if ( $outfilename !== '' ) {
108
			$this->outputfile = fopen( $outfilename, 'w' );
109
			if ( !$this->outputfile ) { // TODO Rather throw an exception here.
110
				print "\nCannot open \"$outfilename\" for writing.\n";
111
				return false;
112
			}
113
		}
114 12
		return true;
115
	}
116
117
	/**
118
	 * Serialize data associated to a specific page. This method works on the
119
	 * level of pages, i.e. it serialises parts of SMW content and implements
120
	 * features like recursive export or backlinks that are available for this
121
	 * type of data.
122
	 *
123
	 * The recursion depth means the following. Depth of 1 or above means
124
	 * the object is serialised with all property values, and referenced
125
	 * objects are serialised with depth reduced by 1. Depth 0 means that only
126
	 * minimal declarations are serialised, so no dependencies are added. A
127
	 * depth of -1 encodes "infinite" depth, i.e. a complete recursive
128
	 * serialisation without limit.
129
	 *
130
	 * @param SMWDIWikiPage $diWikiPage specifying the page to be exported
131
	 * @param integer $recursiondepth specifying the depth of recursion
132
	 */
133 11
	protected function serializePage( SMWDIWikiPage $diWikiPage, $recursiondepth = 1 ) {
134
135 11
		if ( $this->isPageDone( $diWikiPage, $recursiondepth ) ) {
136
			return; // do not export twice
137
		}
138
139 11
		$this->markPageAsDone( $diWikiPage, $recursiondepth );
140 11
		$semData = $this->getSemanticData( $diWikiPage, ( $recursiondepth == 0 ) );
141
142
		// Don't try to serialize an empty page that cause an incomplete exp-data set
143
		// (e.g. _REDI as no property page hence DBKey is empty)
144 11
		if ( $semData === null || $diWikiPage->getDBKey() === '' ) {
145 1
			return null;
146
		}
147
148 11
		$expData = SMWExporter::getInstance()->makeExportData( $semData );
149 11
		$this->serializer->serializeExpData( $expData, $recursiondepth );
0 ignored issues
show
The call to SMWSerializer::serializeExpData() has too many arguments starting with $recursiondepth.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
150
151 11
		foreach( $semData->getSubSemanticData() as $subobjectSemData ) {
152 4
			$this->serializer->serializeExpData( SMWExporter::getInstance()->makeExportData( $subobjectSemData ) );
153
		}
154
155
		// let other extensions add additional RDF data for this page
156 11
		$additionalDataArray = array();
157 11
		\Hooks::run( 'smwAddToRDFExport', array( $diWikiPage, &$additionalDataArray, ( $recursiondepth != 0 ), $this->add_backlinks ) );
158 11
		foreach ( $additionalDataArray as $additionalData ) {
159
			$this->serializer->serializeExpData( $additionalData ); // serialise
160
		}
161
162 11
		if ( $recursiondepth != 0 ) {
163 11
			$subrecdepth = $recursiondepth > 0 ? ( $recursiondepth - 1 ) :
164 11
			               ( $recursiondepth == 0 ? 0 : -1 );
165
166 11
			foreach ( $expData->getProperties() as $property ) {
167 11
				if ( $property->getDataItem() instanceof SMWWikiPageValue ) {
168
					$this->queuePage( $property->getDataItem(), 0 ); // no real recursion along properties
169
				}
170 11
				$wikipagevalues = false;
171 11
				foreach ( $expData->getValues( $property ) as $valueExpElement ) {
172 11
					$valueResource = $valueExpElement instanceof SMWExpData ? $valueExpElement->getSubject() : $valueExpElement;
173 11
					if ( !$wikipagevalues && ( $valueResource->getDataItem() instanceof SMWWikiPageValue ) ) {
174
						$wikipagevalues = true;
175 11
					} elseif ( !$wikipagevalues ) {
176 11
						break;
177
					}
178 11
					$this->queuePage( $valueResource->getDataItem(), $subrecdepth );
179
				}
180
			}
181
182
			// Add backlinks:
183
			// Note: Backlinks are different from recursive serialisations, since
184
			// stub declarations (recdepth==0) still need to have the property that
185
			// links back to the object. So objects that would be exported with
186
			// recdepth 0 cannot be put into the main queue but must be done right
187
			// away. They also might be required many times, if they link back to
188
			// many different objects in many ways (we cannot consider them "Done"
189
			// if they were serialised at recdepth 0 only).
190 11
			if ( $this->add_backlinks ) {
191 6
				$inprops = \SMW\StoreFactory::getStore()->getInProperties( $diWikiPage );
192
193 6
				foreach ( $inprops as $inprop ) {
194 4
					$propWikiPage = $inprop->getDiWikiPage();
195
196 4
					if ( !is_null( $propWikiPage ) ) {
197 4
						$this->queuePage( $propWikiPage, 0 ); // no real recursion along properties
198
					}
199
200 4
					$inSubs = \SMW\StoreFactory::getStore()->getPropertySubjects( $inprop, $diWikiPage );
201
202 4
					foreach ( $inSubs as $inSub ) {
203 4
						if ( !$this->isPageDone( $inSub, $subrecdepth ) ) {
204 4
							$semdata = $this->getSemanticData( $inSub, true );
205
206 4
							if ( !$semdata instanceof SMWSemanticData ) {
207
								continue;
208
							}
209
210 4
							$semdata->addPropertyObjectValue( $inprop, $diWikiPage );
211 4
							$expData = SMWExporter::getInstance()->makeExportData( $semdata );
212 4
							$this->serializer->serializeExpData( $expData, $subrecdepth );
0 ignored issues
show
The call to SMWSerializer::serializeExpData() has too many arguments starting with $subrecdepth.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
213
						}
214
					}
215
				}
216
217 6
				if ( NS_CATEGORY === $diWikiPage->getNamespace() ) { // also print elements of categories
218 1
					$options = new SMWRequestOptions();
219 1
					$options->limit = 100; // Categories can be large, always use limit
220 1
					$instances = \SMW\StoreFactory::getStore()->getPropertySubjects( new SMW\DIProperty( '_INST' ), $diWikiPage, $options );
221 1
					$pinst = new SMW\DIProperty( '_INST' );
222
223 1
					foreach ( $instances as $instance ) {
224
						if ( !array_key_exists( $instance->getHash(), $this->element_done ) ) {
225
							$semdata = $this->getSemanticData( $instance, true );
226
227
							if ( !$semdata instanceof SMWSemanticData ) {
228
								continue;
229
							}
230
231
							$semdata->addPropertyObjectValue( $pinst, $diWikiPage );
232
							$expData = SMWExporter::getInstance()->makeExportData( $semdata );
233 1
							$this->serializer->serializeExpData( $expData, $subrecdepth );
0 ignored issues
show
The call to SMWSerializer::serializeExpData() has too many arguments starting with $subrecdepth.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
234
						}
235
					}
236 5
				} elseif ( SMW_NS_CONCEPT === $diWikiPage->getNamespace() ) { // print concept members (slightly different code)
237
					$desc = new SMWConceptDescription( $diWikiPage );
238
					$desc->addPrintRequest( new PrintRequest( PrintRequest::PRINT_THIS, '' ) );
239
					$query = new SMWQuery( $desc );
240
					$query->setLimit( 100 );
241
242
					$res = \SMW\StoreFactory::getStore()->getQueryResult( $query );
243
					$resarray = $res->getNext();
244
					$pinst = new SMW\DIProperty( '_INST' );
245
246
					while ( $resarray !== false ) {
247
						$instance = end( $resarray )->getNextDataItem();
248
249
						if ( !$instance instanceof \SMWDataItem ) {
250
							$resarray = $res->getNext();
251
							continue;
252
						}
253
254
						if ( !array_key_exists( $instance->getHash(), $this->element_done ) ) {
255
							$semdata = $this->getSemanticData( $instance, true );
256
257
							if ( !$semdata instanceof \SMW\SemanticData ) {
258
								$resarray = $res->getNext();
259
								continue;
260
							}
261
262
							$semdata->addPropertyObjectValue( $pinst, $diWikiPage );
263
							$expData = SMWExporter::getInstance()->makeExportData( $semdata );
264
							$this->serializer->serializeExpData( $expData );
265
						}
266
267
						$resarray = $res->getNext();
268
					}
269
				}
270
			}
271
		}
272 11
	}
273
274
	/**
275
	 * Add a given SMWDIWikiPage to the export queue if needed.
276
	 */
277 11
	protected function queuePage( SMWDIWikiPage $diWikiPage, $recursiondepth ) {
278 11
		if ( !$this->isPageDone( $diWikiPage, $recursiondepth ) ) {
279 11
			$diWikiPage->recdepth = $recursiondepth; // add a field
280 11
			$this->element_queue[$diWikiPage->getHash()] = $diWikiPage;
281
		}
282 11
	}
283
284
	/**
285
	 * Mark an article as done while making sure that the cache used for this
286
	 * stays reasonably small. Input is given as an SMWDIWikiPage object.
287
	 */
288 11
	protected function markPageAsDone( SMWDIWikiPage $di, $recdepth ) {
289 11
		$this->markHashAsDone( $di->getHash(), $recdepth );
290 11
	}
291
292
	/**
293
	 * Mark a task as done while making sure that the cache used for this
294
	 * stays reasonably small.
295
	 */
296 11
	protected function markHashAsDone( $hash, $recdepth ) {
297 11
		if ( count( $this->element_done ) >= self::MAX_CACHE_SIZE ) {
298
			$this->element_done = array_slice( $this->element_done,
299
				self::CACHE_BACKJUMP,
300
				self::MAX_CACHE_SIZE - self::CACHE_BACKJUMP,
301
				true );
302
		}
303 11
		if ( !$this->isHashDone( $hash, $recdepth ) ) {
304 11
			$this->element_done[$hash] = $recdepth; // mark title as done, with given recursion
305
		}
306 11
		unset( $this->element_queue[$hash] ); // make sure it is not in the queue
307 11
	}
308
309
	/**
310
	 * Check if the given object has already been serialised at sufficient
311
	 * recursion depth.
312
	 * @param SMWDIWikiPage $st specifying the object to check
313
	 *
314
	 * @return boolean
315
	 */
316 11
	protected function isPageDone( SMWDIWikiPage $di, $recdepth ) {
317 11
		return $this->isHashDone( $di->getHash(), $recdepth );
318
	}
319
320
	/**
321
	 * Check if the given task has already been completed at sufficient
322
	 * recursion depth.
323
	 */
324 11
	protected function isHashDone( $hash, $recdepth ) {
325 11
		return ( ( array_key_exists( $hash, $this->element_done ) ) &&
326 1
		         ( ( $this->element_done[$hash] == -1 ) ||
327 11
		           ( ( $recdepth != -1 ) && ( $this->element_done[$hash] >= $recdepth ) ) ) );
328
	}
329
330
	/**
331
	 * Retrieve a copy of the semantic data for a wiki page, possibly filtering
332
	 * it so that only essential properties are included (in some cases, we only
333
	 * want to export stub information about a page).
334
	 * We make a copy of the object since we may want to add more data later on
335
	 * and we do not want to modify the store's result which may be used for
336
	 * caching purposes elsewhere.
337
	 */
338 11
	protected function getSemanticData( SMWDIWikiPage $diWikiPage, $core_props_only ) {
339
340
		// Issue 619
341
		// Resolve the redirect target and return a container with information
342
		// about the redirect
343 11
		if ( $diWikiPage->getTitle() !== null && $diWikiPage->getTitle()->isRedirect() ) {
344
345
			try {
346 1
				$redirectTarget = $this->getDeepRedirectTargetResolver()->findRedirectTargetFor( $diWikiPage->getTitle() );
347 1
			} catch ( \Exception $e ) {
348 1
				$redirectTarget = null;
349
			}
350
351
			// Couldn't resolve the redirect which is most likely caused by a
352
			// circular redirect therefore we give up
353 1
			if ( $redirectTarget === null ) {
354 1
				return null;
355
			}
356
357 1
			$semData = new SemanticData( $diWikiPage );
358
359 1
			$semData->addPropertyObjectValue(
360 1
				new DIProperty( '_REDI' ),
361 1
				DIWikiPage::newFromTitle( $redirectTarget )
362
			);
363
364 1
			return $semData;
365
		}
366
367 11
		$semdata = \SMW\StoreFactory::getStore()->getSemanticData( $diWikiPage, $core_props_only ? array( '__spu', '__typ', '__imp' ) : false ); // advise store to retrieve only core things
368 11
		if ( $core_props_only ) { // be sure to filter all non-relevant things that may still be present in the retrieved
369 4
			$result = new SMWSemanticData( $diWikiPage );
370 4
			foreach ( array( '_URI', '_TYPE', '_IMPO' ) as $propid ) {
371 4
				$prop = new SMW\DIProperty( $propid );
372 4
				$values = $semdata->getPropertyValues( $prop );
373 4
				foreach ( $values as $dv ) {
374 4
					$result->addPropertyObjectValue( $prop, $dv );
375
				}
376
			}
377
		} else {
378 11
			$result = clone $semdata;
379
		}
380
381
382 11
		return $result;
383
	}
384
385
	/**
386
	 * Send to the output what has been serialized so far. The flush might
387
	 * be deferred until later unless $force is true.
388
	 */
389 12
	protected function flush( $force = false ) {
390 12
		if ( !$force && ( $this->delay_flush > 0 ) ) {
391 11
			$this->delay_flush -= 1;
392 12
		} elseif ( !is_null( $this->outputfile ) ) {
393
			fwrite( $this->outputfile, $this->serializer->flushContent() );
394
		} else {
395 12
			ob_start();
396 12
			print $this->serializer->flushContent();
397
			// Ship data in small chunks (even though browsers often do not display anything
398
			// before the file is complete -- this might be due to syntax highlighting features
399
			// for app/xml). You may want to sleep(1) here for debugging this.
400 12
			ob_flush();
401 12
			flush();
402 12
			ob_get_clean();
403
		}
404 12
	}
405
406
	/**
407
	 * This function prints all selected pages, specified as an array of page
408
	 * names (strings with namespace identifiers).
409
	 *
410
	 * @param array $pages list of page names to export
411
	 * @param integer $recursion determines how pages are exported recursively:
412
	 * "0" means that referenced resources are only declared briefly, "1" means
413
	 * that all referenced resources are also exported recursively (propbably
414
	 * retrieving the whole wiki).
415
	 * @param string $revisiondate filter page list by including only pages
416
	 * that have been changed since this date; format "YmdHis"
417
	 *
418
	 * @todo Consider dropping the $revisiondate filtering and all associated
419
	 * functionality. Is anybody using this?
420
	 */
421 11
	public function printPages( $pages, $recursion = 1, $revisiondate = false  ) {
422
423 11
		$linkCache = LinkCache::singleton();
424 11
		$this->prepareSerialization();
425 11
		$this->delay_flush = 10; // flush only after (fully) printing 11 objects
426
427
		// transform pages into queued short titles
428 11
		foreach ( $pages as $page ) {
429 11
			$title = Title::newFromText( $page );
430 11
			if ( null === $title ) {
431
				continue; // invalid title name given
432
			}
433 11
			if ( $revisiondate !== '' ) { // filter page list by revision date
434 11
				$rev = Revision::getTimeStampFromID( $title, $title->getLatestRevID() );
435 11
				if ( $rev < $revisiondate ) {
436
					continue;
437
				}
438
			}
439
440 11
			$diPage = SMWDIWikiPage::newFromTitle( $title );
441 11
			$this->queuePage( $diPage, ( $recursion==1 ? -1 : 1 ) );
442
		}
443
444 11
		$this->serializer->startSerialization();
445
446 11
		if ( count( $pages ) == 1 ) { // ensure that ontologies that are retrieved as linked data are not confused with their subject!
447 9
			$ontologyuri = SMWExporter::getInstance()->expandURI( '&export;' ) . '/' . urlencode( end( $pages ) );
448
		} else { // use empty URI, i.e. "location" as URI otherwise
449 4
			$ontologyuri = '';
450
		}
451 11
		$this->serializer->serializeExpData( SMWExporter::getInstance()->getOntologyExpData( $ontologyuri ) );
452
453 11
		while ( count( $this->element_queue ) > 0 ) {
454 11
			$diPage = reset( $this->element_queue );
455 11
			$this->serializePage( $diPage, $diPage->recdepth );
456 11
			$this->flush();
457 11
			$linkCache->clear(); // avoid potential memory leak
458
		}
459 11
		$this->serializer->finishSerialization();
460 11
		$this->flush( true );
461
462 11
	}
463
464
	/**
465
	 * Exports semantic data for all pages within the wiki and for all elements
466
	 * that are referred to a file resource
467
	 *
468
	 * @since  2.0
469
	 *
470
	 * @param string $outfile the output file URI, or false if printing to stdout
471
	 * @param mixed $ns_restriction namespace restriction, see fitsNsRestriction()
472
	 * @param integer $delay number of microseconds for which to sleep during
473
	 * export to reduce server load in long-running operations
474
	 * @param integer $delayeach number of pages to process between two sleeps
475
	 */
476
	public function printAllToFile( $outfile, $ns_restriction = false, $delay, $delayeach ) {
477
478
		if ( !$this->prepareSerialization( $outfile ) ) {
479
			return;
480
		}
481
482
		$this->printAll( $ns_restriction, $delay, $delayeach );
483
	}
484
485
	/**
486
	 * Exports semantic data for all pages within the wiki and for all elements
487
	 * that are referred to the stdout
488
	 *
489
	 * @since  2.0
490
	 *
491
	 * @param mixed $ns_restriction namespace restriction, see fitsNsRestriction()
492
	 * @param integer $delay number of microseconds for which to sleep during
493
	 * export to reduce server load in long-running operations
494
	 * @param integer $delayeach number of pages to process between two sleeps
495
	 */
496 1
	public function printAllToOutput( $ns_restriction = false, $delay, $delayeach ) {
497 1
		$this->prepareSerialization();
498 1
		$this->printAll( $ns_restriction, $delay, $delayeach );
499 1
	}
500
501
	/**
502
	 * @since 2.0 made protected; use printAllToFile or printAllToOutput
503
	 */
504 1
	protected function printAll( $ns_restriction = false, $delay, $delayeach ) {
505 1
		$linkCache = LinkCache::singleton();
506 1
		$db = wfGetDB( DB_SLAVE );
507
508 1
		$this->delay_flush = 10;
509
510 1
		$this->serializer->startSerialization();
511 1
		$this->serializer->serializeExpData( SMWExporter::getInstance()->getOntologyExpData( '' ) );
512
513 1
		$end = $db->selectField( 'page', 'max(page_id)', false, __METHOD__ );
514 1
		$a_count = 0; // DEBUG
515 1
		$d_count = 0; // DEBUG
516 1
		$delaycount = $delayeach;
517
518 1
		for ( $id = 1; $id <= $end; $id += 1 ) {
519 1
			$title = Title::newFromID( $id );
520 1
			if ( is_null( $title ) || !smwfIsSemanticsProcessed( $title->getNamespace() ) ) {
521 1
				continue;
522
			}
523 1
			if ( !self::fitsNsRestriction( $ns_restriction, $title->getNamespace() ) ) {
524
				continue;
525
			}
526 1
			$a_count += 1; // DEBUG
527
528 1
			$diPage = SMWDIWikiPage::newFromTitle( $title );
529 1
			$this->queuePage( $diPage, 1 );
530
531 1
			while ( count( $this->element_queue ) > 0 ) {
532 1
				$diPage = reset( $this->element_queue );
533 1
				$this->serializePage( $diPage, $diPage->recdepth );
534
				// resolve dependencies that will otherwise not be printed
535 1
				foreach ( $this->element_queue as $key => $diaux ) {
536
					if ( !smwfIsSemanticsProcessed( $diaux->getNamespace() ) ||
537
					     !self::fitsNsRestriction( $ns_restriction, $diaux->getNamespace() ) ) {
538
						// Note: we do not need to check the cache to guess if an element was already
539
						// printed. If so, it would not be included in the queue in the first place.
540
						$d_count += 1; // DEBUG
541
					} else { // don't carry values that you do not want to export (yet)
542
						unset( $this->element_queue[$key] );
543
					}
544
				}
545
				// sleep each $delaycount for $delay µs to be nice to the server
546 1
				if ( ( $delaycount-- < 0 ) && ( $delayeach != 0 ) ) {
547
					usleep( $delay );
548
					$delaycount = $delayeach;
549
				}
550
			}
551
552 1
			$this->flush();
553 1
			$linkCache->clear();
554
		}
555
556 1
		$this->serializer->finishSerialization();
557 1
		$this->flush( true );
558 1
	}
559
560
	/**
561
	 * Print basic definitions a list of pages ordered by their page id.
562
	 * Offset and limit refer to the count of existing pages, not to the
563
	 * page id.
564
	 * @param integer $offset the number of the first (existing) page to
565
	 * serialize a declaration for
566
	 * @param integer $limit the number of pages to serialize
567
	 */
568
	public function printPageList( $offset = 0, $limit = 30 ) {
569
		global $smwgNamespacesWithSemanticLinks;
570
571
		$db = wfGetDB( DB_SLAVE );
572
		$this->prepareSerialization();
573
		$this->delay_flush = 35; // don't do intermediate flushes with default parameters
574
		$linkCache = LinkCache::singleton();
575
576
		$this->serializer->startSerialization();
577
		$this->serializer->serializeExpData( SMWExporter::getInstance()->getOntologyExpData( '' ) );
578
579
		$query = '';
580
		foreach ( $smwgNamespacesWithSemanticLinks as $ns => $enabled ) {
581
			if ( $enabled ) {
582
				if ( $query !== '' ) {
583
					$query .= ' OR ';
584
				}
585
				$query .= 'page_namespace = ' . $db->addQuotes( $ns );
586
			}
587
		}
588
		$res = $db->select( $db->tableName( 'page' ),
589
		                    'page_id,page_title,page_namespace', $query,
590
		                    'SMW::RDF::PrintPageList', array( 'ORDER BY' => 'page_id ASC', 'OFFSET' => $offset, 'LIMIT' => $limit ) );
591
		$foundpages = false;
592
593
		foreach ( $res as $row ) {
594
			$foundpages = true;
595
			try {
596
				$diPage = new SMWDIWikiPage( $row->page_title, $row->page_namespace, '' );
597
				$this->serializePage( $diPage, 0 );
598
				$this->flush();
599
				$linkCache->clear();
600
			} catch ( SMWDataItemException $e ) {
601
				// strange data, who knows, not our DB table, keep calm and carry on
602
			}
603
		}
604
605
		if ( $foundpages ) { // add link to next result page
606
			if ( strpos( SMWExporter::getInstance()->expandURI( '&wikiurl;' ), '?' ) === false ) { // check whether we have title as a first parameter or in URL
607
				$nexturl = SMWExporter::getInstance()->expandURI( '&export;?offset=' ) . ( $offset + $limit );
608
			} else {
609
				$nexturl = SMWExporter::getInstance()->expandURI( '&export;&amp;offset=' ) . ( $offset + $limit );
610
			}
611
612
			$expData = new SMWExpData( new SMWExpResource( $nexturl ) );
613
			$ed = new SMWExpData( SMWExporter::getInstance()->getSpecialNsResource( 'owl', 'Thing' ) );
614
			$expData->addPropertyObjectValue( SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'type' ), $ed );
615
			$ed = new SMWExpData( new SMWExpResource( $nexturl ) );
616
			$expData->addPropertyObjectValue( SMWExporter::getInstance()->getSpecialNsResource( 'rdfs', 'isDefinedBy' ), $ed );
617
			$this->serializer->serializeExpData( $expData );
618
		}
619
620
		$this->serializer->finishSerialization();
621
		$this->flush( true );
622
623
	}
624
625
626
	/**
627
	 * Print basic information about this site.
628
	 */
629 1
	public function printWikiInfo() {
630
631 1
		global $wgSitename, $wgLanguageCode;
632
633 1
		$this->prepareSerialization();
634 1
		$this->delay_flush = 35; // don't do intermediate flushes with default parameters
635
636
		// assemble export data:
637 1
		$expData = new SMWExpData( new SMWExpResource( '&wiki;#wiki' ) );
638
639 1
		$expData->addPropertyObjectValue(
640 1
			SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'type' ),
641 1
			new SMWExpData( SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'Wikisite' ) )
642
		);
643
644
		// basic wiki information
645 1
		$expData->addPropertyObjectValue(
646 1
			SMWExporter::getInstance()->getSpecialNsResource( 'rdfs', 'label' ),
647 1
			new SMWExpLiteral( $wgSitename )
648
		);
649
650 1
		$expData->addPropertyObjectValue(
651 1
			SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'siteName' ),
652 1
			new SMWExpLiteral( $wgSitename, 'http://www.w3.org/2001/XMLSchema#string' )
653
		);
654
655 1
		$expData->addPropertyObjectValue(
656 1
			SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'pagePrefix' ),
657 1
			new SMWExpLiteral( SMWExporter::getInstance()->expandURI( '&wikiurl;' ), 'http://www.w3.org/2001/XMLSchema#string' )
658
		);
659
660 1
		$expData->addPropertyObjectValue(
661 1
			SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'smwVersion' ),
662 1
			new SMWExpLiteral( SMW_VERSION, 'http://www.w3.org/2001/XMLSchema#string' )
663
		);
664
665 1
		$expData->addPropertyObjectValue(
666 1
			SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'langCode' ),
667 1
			new SMWExpLiteral( $wgLanguageCode, 'http://www.w3.org/2001/XMLSchema#string' )
668
		);
669
670 1
		$mainpage = Title::newMainPage();
671
672 1
		if ( !is_null( $mainpage ) ) {
673 1
			$ed = new SMWExpData( new SMWExpResource( $mainpage->getFullURL() ) );
674 1
			$expData->addPropertyObjectValue( SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'mainPage' ), $ed );
675
		}
676
677
		// statistical information
678 1
		$expData->addPropertyObjectValue(
679 1
			SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'pageCount' ),
680 1
			new SMWExpLiteral( SiteStats::pages(), 'http://www.w3.org/2001/XMLSchema#int' )
681
		);
682
683 1
		$expData->addPropertyObjectValue(
684 1
			SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'contentPageCount' ),
685 1
			new SMWExpLiteral( SiteStats::articles(), 'http://www.w3.org/2001/XMLSchema#int' )
686
		);
687
688 1
		$expData->addPropertyObjectValue(
689 1
			SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'mediaCount' ),
690 1
			new SMWExpLiteral( SiteStats::images(), 'http://www.w3.org/2001/XMLSchema#int' )
691
		);
692
693 1
		$expData->addPropertyObjectValue(
694 1
			SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'editCount' ),
695 1
			new SMWExpLiteral( SiteStats::edits(), 'http://www.w3.org/2001/XMLSchema#int' )
696
		);
697
698
		// SiteStats::views was deprecated in MediaWiki 1.25
699
		// "Stop calling this function, it will be removed some time in the future"
700
		//$expData->addPropertyObjectValue(
701
		//	SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'viewCount' ),
702
		//	new SMWExpLiteral( SiteStats::views(), 'http://www.w3.org/2001/XMLSchema#int' )
703
		//);
704
705 1
		$expData->addPropertyObjectValue(
706 1
			SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'userCount' ),
707 1
			new SMWExpLiteral( SiteStats::users(), 'http://www.w3.org/2001/XMLSchema#int' )
708
		);
709
710 1
		$expData->addPropertyObjectValue(
711 1
			SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'adminCount' ),
712 1
			new SMWExpLiteral( SiteStats::numberingroup( 'sysop' ), 'http://www.w3.org/2001/XMLSchema#int' )
713
		);
714
715 1
		$this->serializer->startSerialization();
716 1
		$this->serializer->serializeExpData( SMWExporter::getInstance()->getOntologyExpData( '' ) );
717 1
		$this->serializer->serializeExpData( $expData );
718
719
		// link to list of existing pages:
720 1
		if ( strpos( SMWExporter::getInstance()->expandURI( '&wikiurl;' ), '?' ) === false ) { // check whether we have title as a first parameter or in URL
721 1
			$nexturl = SMWExporter::getInstance()->expandURI( '&export;?offset=0' );
722
		} else {
723
			$nexturl = SMWExporter::getInstance()->expandURI( '&export;&amp;offset=0' );
724
		}
725 1
		$expData = new SMWExpData( new SMWExpResource( $nexturl ) );
726 1
		$ed = new SMWExpData( SMWExporter::getInstance()->getSpecialNsResource( 'owl', 'Thing' ) );
727 1
		$expData->addPropertyObjectValue( SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'type' ), $ed );
728 1
		$ed = new SMWExpData( new SMWExpResource( $nexturl ) );
729 1
		$expData->addPropertyObjectValue( SMWExporter::getInstance()->getSpecialNsResource( 'rdfs', 'isDefinedBy' ), $ed );
730 1
		$this->serializer->serializeExpData( $expData );
731
732 1
		$this->serializer->finishSerialization();
733 1
		$this->flush( true );
734
735 1
	}
736
737
	/**
738
	 * This function checks whether some article fits into a given namespace
739
	 * restriction. Restrictions are encoded as follows: a non-negative number
740
	 * requires the namespace to be identical to the given number; "-1"
741
	 * requires the namespace to be different from Category, Property, and
742
	 * Type; "false" means "no restriction".
743
	 *
744
	 * @param $res mixed encoding the restriction as described above
745
	 * @param $ns integer the namespace constant to be checked
746
	 *
747
	 * @return boolean
748
	 */
749 1
	static public function fitsNsRestriction( $res, $ns ) {
750 1
		if ( $res === false ) {
751 1
			return true;
752
		}
753
		if ( is_array( $res ) ) {
754
			return in_array( $ns, $res );
755
		}
756
		if ( $res >= 0 ) {
757
			return ( $res == $ns );
758
		}
759
		return ( ( $res != NS_CATEGORY ) && ( $res != SMW_NS_PROPERTY ) && ( $res != SMW_NS_TYPE ) );
760
	}
761
762 1
	private function getDeepRedirectTargetResolver() {
763
764 1
		if ( $this->deepRedirectTargetResolver === null ) {
765 1
			$this->deepRedirectTargetResolver = ApplicationFactory::getInstance()->newMwCollaboratorFactory()->newDeepRedirectTargetResolver();
766
		}
767
768 1
		return $this->deepRedirectTargetResolver;
769
	}
770
771
}
772