67 parent::__construct();
68 $gz = in_array(
'compress.zlib', stream_get_wrappers() )
70 :
'(disabled; requires PHP zlib module)';
71 $bz2 = in_array(
'compress.bzip2', stream_get_wrappers() )
73 :
'(disabled; requires PHP bzip2 module)';
77This script reads pages from an XML file as produced from Special:Export or
78dumpBackup.php, and saves them into the current wiki.
80Compressed XML files may be read directly:
83 .7z (
if 7za executable is in PATH)
85Note that
for very large data sets, importDump.php may be slow; there are
86alternate methods which can be much faster
for full site restoration:
90 $this->stderr = fopen(
"php://stderr",
"wt" );
92 'Report position and speed after every n pages processed',
false,
true );
94 'Import only the pages from namespaces belonging to the list of ' .
95 'pipe-separated namespace names or namespace indexes',
false,
true );
96 $this->
addOption(
'rootpage',
'Pages will be imported as subpages of the specified page',
98 $this->
addOption(
'dry-run',
'Parse dump without actually importing pages' );
99 $this->
addOption(
'debug',
'Output extra verbose debug information' );
100 $this->
addOption(
'uploads',
'Process file upload data if included (experimental)' );
103 'Disable link table updates. Is faster but leaves the wiki in an inconsistent state'
105 $this->
addOption(
'image-base-path',
'Import files from a specified path',
false,
true );
106 $this->
addOption(
'skip-to',
'Start from nth page by skipping first n-1 pages',
false,
true );
108 'Prefix for interwiki usernames; a trailing ">" will be added. Default: "imported>"',
111 'Treat all usernames as interwiki. ' .
112 'The default is to assign edits to local users where they exist.',
115 $this->
addArg(
'file',
'Dump file to import [else use stdin]',
false );
120 $this->
fatalError(
"Wiki is in read-only mode; you'll need to disable it for import to work." );
123 $this->reportingInterval = intval( $this->
getOption(
'report', 100 ) );
124 if ( !$this->reportingInterval ) {
126 $this->reportingInterval = 100;
129 $this->dryRun = $this->
hasOption(
'dry-run' );
130 $this->uploads = $this->
hasOption(
'uploads' );
132 if ( $this->
hasOption(
'image-base-path' ) ) {
133 $this->imageBasePath = $this->
getOption(
'image-base-path' );
135 if ( $this->
hasOption(
'namespaces' ) ) {
136 $this->setNsfilter( explode(
'|', $this->
getOption(
'namespaces' ) ) );
139 if ( $this->
hasArg( 0 ) ) {
140 $this->importFromFile( $this->
getArg( 0 ) );
142 $this->importFromStdin();
145 $this->
output(
"Done!\n" );
146 $this->
output(
"You might want to run rebuildrecentchanges.php to regenerate RecentChanges,\n" );
147 $this->
output(
"and initSiteStats.php to update page and revision counts\n" );
150 private function setNsfilter( array $namespaces ) {
151 if ( count( $namespaces ) == 0 ) {
152 $this->nsFilter =
false;
156 $this->nsFilter = array_unique( array_map( [ $this,
'getNsIndex' ], $namespaces ) );
159 private function getNsIndex( $namespace ) {
161 $result = $contLang->getNsIndex( $namespace );
162 if ( $result !==
false ) {
165 $ns = intval( $namespace );
166 if ( strval( $ns ) === $namespace && $contLang->getNsText( $ns ) !==
false ) {
169 $this->
fatalError(
"Unknown namespace text / index specified: $namespace" );
176 private function skippedNamespace( $title ) {
177 if ( $title ===
null ) {
182 $ns = $title->getNamespace();
184 return is_array( $this->nsFilter ) && !in_array( $ns, $this->nsFilter );
197 $this->progress(
"Got bogus revision with null title!" );
202 if ( $this->skippedNamespace( $title ) ) {
209 if ( !$this->dryRun ) {
210 call_user_func( $this->importCallback, $rev );
219 if ( $this->uploads ) {
220 if ( $this->skippedNamespace( $revision->
getTitle() ) ) {
223 $this->uploadCount++;
225 $this->progress(
"upload: " . $revision->
getFilename() );
227 if ( !$this->dryRun ) {
231 $statusValue = $importer->import( $revision );
233 return $statusValue->isGood();
244 if ( $this->skippedNamespace( $rev->
getTitle() ) ) {
250 if ( !$this->dryRun ) {
251 call_user_func( $this->logItemCallback, $rev );
255 private function report( $final =
false ) {
256 if ( $final xor ( $this->pageCount % $this->reportingInterval == 0 ) ) {
261 private function showReport() {
262 if ( !$this->mQuiet ) {
265 $rate = sprintf(
"%.2f", $this->pageCount / $delta );
266 $revrate = sprintf(
"%.2f", $this->revCount / $delta );
271 # Logs dumps don't have page tallies
272 if ( $this->pageCount ) {
273 $this->progress(
"$this->pageCount ($rate pages/sec $revrate revs/sec)" );
275 $this->progress(
"$this->revCount ($revrate revs/sec)" );
281 private function progress( $string ) {
282 fwrite( $this->stderr, $string .
"\n" );
285 private function importFromFile( $filename ) {
286 if ( preg_match(
'/\.gz$/', $filename ) ) {
287 $filename =
'compress.zlib://' . $filename;
288 } elseif ( preg_match(
'/\.bz2$/', $filename ) ) {
289 $filename =
'compress.bzip2://' . $filename;
290 } elseif ( preg_match(
'/\.7z$/', $filename ) ) {
291 $filename =
'mediawiki.compress.7z://' . $filename;
294 $file = fopen( $filename,
'rt' );
295 if ( $file ===
false ) {
296 $this->
fatalError( error_get_last()[
'message'] ??
'Could not open file' );
299 return $this->importFromHandle( $file );
302 private function importFromStdin() {
303 $file = fopen(
'php://stdin',
'rt' );
304 if ( self::posix_isatty( $file ) ) {
308 return $this->importFromHandle( $file );
311 private function importFromHandle( $handle ) {
312 $this->startTime = microtime(
true );
314 $user = User::newSystemUser( User::MAINTENANCE_SCRIPT_USER, [
'steal' =>
true ] );
318 ->getWikiImporterFactory()
322 $importer->disableStatisticsUpdate();
325 $importer->setDebug(
true );
327 if ( $this->
hasOption(
'no-updates' ) ) {
328 $importer->setNoUpdates(
true );
330 $importer->setUsernamePrefix(
331 $this->
getOption(
'username-prefix',
'imported' ),
335 $statusRootPage = $importer->setTargetRootPage( $this->
getOption(
'rootpage' ) );
336 if ( !$statusRootPage->isGood() ) {
342 $nthPage = (int)$this->
getOption(
'skip-to' );
343 $importer->setPageOffset( $nthPage );
344 $this->pageCount = $nthPage - 1;
346 $importer->setPageCallback( [ $this,
'reportPage' ] );
347 $importer->setNoticeCallback(
static function ( $msg,
$params ) {
350 $this->importCallback = $importer->setRevisionCallback(
351 [ $this,
'handleRevision' ] );
352 $this->uploadCallback = $importer->setUploadCallback(
353 [ $this,
'handleUpload' ] );
354 $this->logItemCallback = $importer->setLogItemCallback(
355 [ $this,
'handleLogItem' ] );
356 if ( $this->uploads ) {
357 $importer->setImportUploads(
true );
359 if ( $this->imageBasePath ) {
360 $importer->setImageBasePath( $this->imageBasePath );
363 if ( $this->dryRun ) {
364 $importer->setPageOutCallback(
null );
367 return $importer->doImport();
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
addArg( $arg, $description, $required=true, $multi=false)
Add some args that are needed.
output( $out, $channel=null)
Throw some output to the user.
hasArg( $argId=0)
Does a given argument exist?
waitForReplication()
Wait for replica DBs to catch up.
hasOption( $name)
Checks to see if a particular option was set.
getServiceContainer()
Returns the main service container.
getArg( $argId=0, $default=null)
Get an argument.
addDescription( $text)
Set the description text.
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
getOption( $name, $default=null)
Get an option, or return the default.
fatalError( $msg, $exitCode=1)
Output a message and terminate the current script.