56 parent::__construct();
57 $gz = in_array(
'compress.zlib', stream_get_wrappers() )
59 :
'(disabled; requires PHP zlib module)';
60 $bz2 = in_array(
'compress.bzip2', stream_get_wrappers() )
62 :
'(disabled; requires PHP bzip2 module)';
66This script reads pages from an XML file as produced from Special:Export or
67dumpBackup.php, and saves them into the current wiki.
69Compressed XML files may be read directly:
72 .7z (
if 7za executable is in PATH)
74Note that
for very large data sets, importDump.php may be slow; there are
75alternate methods which can be much faster
for full site restoration:
79 $this->stderr = fopen(
"php://stderr",
"wt" );
81 'Report position and speed after every n pages processed',
false,
true );
83 'Import only the pages from namespaces belonging to the list of ' .
84 'pipe-separated namespace names or namespace indexes',
false,
true );
85 $this->
addOption(
'rootpage',
'Pages will be imported as subpages of the specified page',
87 $this->
addOption(
'dry-run',
'Parse dump without actually importing pages' );
88 $this->
addOption(
'debug',
'Output extra verbose debug information' );
89 $this->
addOption(
'uploads',
'Process file upload data if included (experimental)' );
92 'Disable link table updates. Is faster but leaves the wiki in an inconsistent state'
94 $this->
addOption(
'image-base-path',
'Import files from a specified path',
false,
true );
95 $this->
addOption(
'skip-to',
'Start from nth page by skipping first n-1 pages',
false,
true );
97 'Prefix for interwiki usernames; a trailing ">" will be added. Default: "imported>"',
100 'Treat all usernames as interwiki. ' .
101 'The default is to assign edits to local users where they exist.',
104 $this->
addArg(
'file',
'Dump file to import [else use stdin]',
false );
109 $this->
fatalError(
"Wiki is in read-only mode; you'll need to disable it for import to work." );
112 $this->reportingInterval = intval( $this->
getOption(
'report', 100 ) );
113 if ( !$this->reportingInterval ) {
115 $this->reportingInterval = 100;
118 $this->dryRun = $this->
hasOption(
'dry-run' );
119 $this->uploads = $this->
hasOption(
'uploads' );
121 if ( $this->
hasOption(
'image-base-path' ) ) {
122 $this->imageBasePath = $this->
getOption(
'image-base-path' );
124 if ( $this->
hasOption(
'namespaces' ) ) {
125 $this->setNsfilter( explode(
'|', $this->
getOption(
'namespaces' ) ) );
128 if ( $this->
hasArg( 0 ) ) {
129 $this->importFromFile( $this->
getArg( 0 ) );
131 $this->importFromStdin();
134 $this->
output(
"Done!\n" );
135 $this->
output(
"You might want to run rebuildrecentchanges.php to regenerate RecentChanges,\n" );
136 $this->
output(
"and initSiteStats.php to update page and revision counts\n" );
139 private function setNsfilter( array $namespaces ) {
140 if ( count( $namespaces ) == 0 ) {
141 $this->nsFilter =
false;
145 $this->nsFilter = array_unique( array_map( $this->getNsIndex( ... ), $namespaces ) );
148 private function getNsIndex(
string $namespace ): int {
150 $result = $contLang->getNsIndex( $namespace );
151 if ( $result !==
false ) {
154 $ns = intval( $namespace );
155 if ( strval( $ns ) === $namespace && $contLang->getNsText( $ns ) !==
false ) {
158 $this->
fatalError(
"Unknown namespace text / index specified: $namespace" );
165 private function skippedNamespace( $title ) {
166 if ( $title ===
null ) {
171 $ns = $title->getNamespace();
173 return is_array( $this->nsFilter ) && !in_array( $ns, $this->nsFilter );
182 $title = $rev->getTitle();
184 $this->progress(
"Got bogus revision with null title!" );
189 if ( $this->skippedNamespace( $title ) ) {
195 if ( !$this->dryRun ) {
196 ( $this->importCallback )( $rev );
205 if ( $this->uploads ) {
206 if ( $this->skippedNamespace( $revision->getTitle() ) ) {
209 $this->uploadCount++;
211 $this->progress(
"upload: " . $revision->getFilename() );
213 if ( !$this->dryRun ) {
216 $importer = $this->getServiceContainer()->getWikiRevisionUploadImporter();
217 $statusValue = $importer->import( $revision );
219 return $statusValue->isGood();
227 if ( $this->skippedNamespace( $rev->getTitle() ) ) {
233 if ( !$this->dryRun ) {
234 ( $this->logItemCallback )( $rev );
238 private function report(
bool $final =
false ) {
239 if ( $final xor ( $this->pageCount % $this->reportingInterval == 0 ) ) {
244 private function showReport() {
245 if ( !$this->mQuiet ) {
246 $delta = microtime(
true ) - $this->startTime;
248 $rate = sprintf(
"%.2f", $this->pageCount / $delta );
249 $revrate = sprintf(
"%.2f", $this->revCount / $delta );
254 # Logs dumps don't have page tallies
255 if ( $this->pageCount ) {
256 $this->progress(
"$this->pageCount ($rate pages/sec $revrate revs/sec)" );
258 $this->progress(
"$this->revCount ($revrate revs/sec)" );
261 $this->waitForReplication();
264 private function progress(
string $string ) {
265 fwrite( $this->stderr, $string .
"\n" );
268 private function importFromFile(
string $filename ): bool {
269 if ( preg_match(
'/\.gz$/', $filename ) ) {
270 $filename =
'compress.zlib://' . $filename;
271 } elseif ( preg_match(
'/\.bz2$/', $filename ) ) {
272 $filename =
'compress.bzip2://' . $filename;
273 } elseif ( preg_match(
'/\.7z$/', $filename ) ) {
274 $filename =
'mediawiki.compress.7z://' . $filename;
277 $file = fopen( $filename,
'rt' );
278 if ( $file ===
false ) {
279 $this->fatalError( error_get_last()[
'message'] ??
'Could not open file' );
282 return $this->importFromHandle( $file );
285 private function importFromStdin(): bool {
286 $file = fopen(
'php://stdin',
'rt' );
287 if ( self::posix_isatty( $file ) ) {
288 $this->maybeHelp(
true );
291 return $this->importFromHandle( $file );
297 private function importFromHandle( $handle ): bool {
298 $this->startTime = microtime( true );
300 $user = User::newSystemUser( User::MAINTENANCE_SCRIPT_USER, [
'steal' =>
true ] );
302 $source =
new ImportStreamSource( $handle );
303 $importer = $this->getServiceContainer()
304 ->getWikiImporterFactory()
308 $importer->disableStatisticsUpdate();
310 if ( $this->hasOption(
'debug' ) ) {
311 $importer->setDebug(
true );
313 if ( $this->hasOption(
'no-updates' ) ) {
314 $importer->setNoUpdates(
true );
316 $importer->setUsernamePrefix(
317 $this->getOption(
'username-prefix',
'imported' ),
318 !$this->hasOption(
'no-local-users' )
320 if ( $this->hasOption(
'rootpage' ) ) {
321 $statusRootPage = $importer->setTargetRootPage( $this->getOption(
'rootpage' ) );
322 if ( !$statusRootPage->isGood() ) {
324 $this->fatalError( $statusRootPage );
327 if ( $this->hasOption(
'skip-to' ) ) {
328 $nthPage = (int)$this->getOption(
'skip-to' );
329 $importer->setPageOffset( $nthPage );
330 $this->pageCount = $nthPage - 1;
332 $importer->setPageCallback( $this->reportPage( ... ) );
333 $importer->setNoticeCallback(
static function ( $msg, $params ) {
334 echo
wfMessage( $msg, $params )->text() .
"\n";
336 $this->importCallback = $importer->setRevisionCallback(
337 $this->handleRevision( ... ) );
338 $this->uploadCallback = $importer->setUploadCallback(
339 $this->handleUpload( ... ) );
340 $this->logItemCallback = $importer->setLogItemCallback(
341 $this->handleLogItem( ... ) );
342 if ( $this->uploads ) {
343 $importer->setImportUploads(
true );
345 if ( $this->imageBasePath ) {
346 $importer->setImageBasePath( $this->imageBasePath );
349 if ( $this->dryRun ) {
350 $importer->setPageOutCallback(
null );
353 return $importer->doImport();