72 private $sourceAdapterId;
75 private $foreignNamespaces =
null;
78 private $mLogItemCallback;
81 private $mUploadCallback;
84 private $mRevisionCallback;
87 private $mPageCallback;
90 private $mSiteInfoCallback;
93 private $mPageOutCallback;
96 private $mNoticeCallback;
102 private $mImportUploads;
105 private $mImageBasePath;
108 private $mNoUpdates =
false;
111 private $pageOffset = 0;
117 private $countableCache = [];
120 private $disableStatisticsUpdate =
false;
156 $this->performer = $performer;
157 $this->config = $config;
158 $this->hookRunner =
new HookRunner( $hookContainer );
159 $this->contentLanguage = $contentLanguage;
160 $this->namespaceInfo = $namespaceInfo;
161 $this->titleFactory = $titleFactory;
162 $this->wikiPageFactory = $wikiPageFactory;
163 $this->uploadRevisionImporter = $uploadRevisionImporter;
164 $this->contentHandlerFactory = $contentHandlerFactory;
165 $this->slotRoleRegistry = $slotRoleRegistry;
167 if ( !in_array(
'uploadsource', stream_get_wrappers() ) ) {
168 stream_wrapper_register(
'uploadsource', UploadSourceAdapter::class );
182 $this->contentLanguage,
183 $this->namespaceInfo,
193 return $this->reader;
200 $this->
debug(
"FAILURE: $err" );
201 wfDebug(
"WikiImporter XML error: $err" );
208 if ( $this->mDebug ) {
216 public function warn( $data ) {
226 public function notice( $msg, ...$params ) {
227 if ( is_callable( $this->mNoticeCallback ) ) {
228 ( $this->mNoticeCallback )( $msg, $params );
241 $this->mDebug = $debug;
249 $this->mNoUpdates = $noupdates;
259 $this->pageOffset = $nthPage;
269 return wfSetVar( $this->mNoticeCallback, $callback );
278 $previous = $this->mPageCallback;
279 $this->mPageCallback = $callback;
293 $previous = $this->mPageOutCallback;
294 $this->mPageOutCallback = $callback;
304 $previous = $this->mRevisionCallback;
305 $this->mRevisionCallback = $callback;
315 $previous = $this->mUploadCallback;
316 $this->mUploadCallback = $callback;
326 $previous = $this->mLogItemCallback;
327 $this->mLogItemCallback = $callback;
337 $previous = $this->mSiteInfoCallback;
338 $this->mSiteInfoCallback = $callback;
348 $this->importTitleFactory = $factory;
357 if ( $namespace ===
null ) {
361 $this->contentLanguage,
362 $this->namespaceInfo,
369 $this->namespaceInfo->exists( intval( $namespace ) )
371 $namespace = intval( $namespace );
374 $this->namespaceInfo,
391 $status = Status::newGood();
392 $nsInfo = $this->namespaceInfo;
393 if ( $rootpage ===
null ) {
397 $this->contentLanguage,
402 } elseif ( $rootpage !==
'' ) {
403 $rootpage = rtrim( $rootpage,
'/' );
404 $title = Title::newFromText( $rootpage );
406 if ( !$title || $title->isExternal() ) {
407 $status->fatal(
'import-rootpage-invalid' );
408 } elseif ( !$nsInfo->hasSubpages( $title->getNamespace() ) ) {
409 $displayNSText = $title->getNamespace() ===
NS_MAIN
411 : $this->contentLanguage->getNsText( $title->getNamespace() );
412 $status->fatal(
'import-rootpage-nosubpage', $displayNSText );
432 $this->mImageBasePath = $dir;
439 $this->mImportUploads = $import;
448 $this->externalUserNames =
new ExternalUserNames( $usernamePrefix, $assignKnownUsers );
466 $title = $titleAndForeignTitle[0];
467 $page = $this->wikiPageFactory->newFromTitle( $title );
468 $this->countableCache[
'title_' . $title->getPrefixedText()] = $page->isCountable();
478 if ( !$revision->getContentHandler()->canBeUsedOn( $revision->getTitle() ) ) {
479 $this->
notice(
'import-error-bad-location',
480 $revision->getTitle()->getPrefixedText(),
482 $revision->getModel(),
483 $revision->getFormat()
490 return $revision->importOldRevision();
492 $this->
notice(
'import-error-unserialize',
493 $revision->getTitle()->getPrefixedText(),
495 $revision->getModel(),
496 $revision->getFormat()
509 return $revision->importLogItem();
518 $status = $this->uploadRevisionImporter->import( $revision );
519 return $status->isGood();
532 $sRevCount, $pageInfo
541 $page = $this->wikiPageFactory->newFromTitle( $pageIdentity );
543 $page->loadPageData( IDBAccessObject::READ_LATEST );
544 $rev = $page->getRevisionRecord();
545 if ( $rev ===
null ) {
547 wfDebug( __METHOD__ .
': Skipping article count adjustment for ' . $pageIdentity .
548 ' because WikiPage::getRevisionRecord() returned null' );
550 $update = $page->newPageUpdater( $this->performer )->prepareUpdate();
551 $countKey =
'title_' . CacheKeyHelper::getKeyForPage( $pageIdentity );
552 $countable = $update->isCountable();
553 if ( array_key_exists( $countKey, $this->countableCache ) &&
554 $countable != $this->countableCache[$countKey] ) {
555 DeferredUpdates::addUpdate( SiteStatsUpdate::factory( [
556 'articles' => ( (
int)$countable - (
int)$this->countableCache[$countKey] )
562 $title = Title::newFromPageIdentity( $pageIdentity );
563 return $this->hookRunner->onAfterImportPage( $title, $foreignTitle,
564 $revCount, $sRevCount, $pageInfo );
572 private function siteInfoCallback( $siteInfo ) {
573 if ( $this->mSiteInfoCallback ) {
574 return ( $this->mSiteInfoCallback )( $siteInfo, $this );
585 if ( $this->mPageCallback ) {
586 ( $this->mPageCallback )( $title );
598 private function pageOutCallback(
PageIdentity $pageIdentity, $foreignTitle, $revCount,
599 $sucCount, $pageInfo ) {
600 if ( $this->mPageOutCallback ) {
601 ( $this->mPageOutCallback )( $pageIdentity, $foreignTitle, $revCount, $sucCount, $pageInfo );
610 private function revisionCallback( $revision ) {
611 if ( $this->mRevisionCallback ) {
612 return ( $this->mRevisionCallback )( $revision, $this );
623 private function logItemCallback( $revision ) {
624 if ( $this->mLogItemCallback ) {
625 return ( $this->mLogItemCallback )( $revision, $this );
638 return $this->reader->getAttribute( $attr ) ??
'';
649 if ( $this->reader->isEmptyElement ) {
653 while ( $this->reader->read() ) {
654 switch ( $this->reader->nodeType ) {
655 case XMLReader::TEXT:
656 case XMLReader::CDATA:
657 case XMLReader::SIGNIFICANT_WHITESPACE:
658 $buffer .= $this->reader->value;
660 case XMLReader::END_ELEMENT:
665 $this->reader->close();
675 $this->syntaxCheckXML();
681 $oldDisable = @libxml_disable_entity_loader(
true );
683 $this->reader->read();
685 if ( $this->reader->localName !=
'mediawiki' ) {
687 @libxml_disable_entity_loader( $oldDisable );
688 $error = libxml_get_last_error();
690 throw new NormalizedException(
"XML error at line {line}: {message}", [
691 'line' => $error->line,
692 'message' => $error->message,
695 throw new UnexpectedValueException(
696 "Expected '<mediawiki>' tag, got '<{$this->reader->localName}>' tag."
700 $this->
debug(
"<mediawiki> tag is correct." );
702 $this->
debug(
"Starting primary dump processing loop." );
704 $keepReading = $this->reader->read();
707 while ( $keepReading ) {
708 $tag = $this->reader->localName;
709 if ( $this->pageOffset ) {
710 if ( $tag ===
'page' ) {
713 if ( $pageCount < $this->pageOffset ) {
714 $keepReading = $this->reader->next();
718 $type = $this->reader->nodeType;
720 if ( !$this->hookRunner->onImportHandleToplevelXMLTag( $this ) ) {
722 } elseif ( $tag ==
'mediawiki' && $type == XMLReader::END_ELEMENT ) {
724 } elseif ( $tag ==
'siteinfo' ) {
725 $this->handleSiteInfo();
726 } elseif ( $tag ==
'page' ) {
728 } elseif ( $tag ==
'logitem' ) {
729 $this->handleLogItem();
730 } elseif ( $tag !=
'#text' ) {
731 $this->
warn(
"Unhandled top-level XML tag $tag" );
737 $keepReading = $this->reader->next();
739 $this->
debug(
"Skip" );
741 $keepReading = $this->reader->read();
746 @libxml_disable_entity_loader( $oldDisable );
747 $this->reader->close();
753 private function handleSiteInfo() {
754 $this->debug(
"Enter site info handler." );
758 $normalFields = [
'sitename',
'base',
'generator',
'case' ];
760 while ( $this->reader->read() ) {
761 if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
762 $this->reader->localName ==
'siteinfo' ) {
766 $tag = $this->reader->localName;
768 if ( $tag ==
'namespace' ) {
771 } elseif ( in_array( $tag, $normalFields ) ) {
776 $siteInfo[
'_namespaces'] = $this->foreignNamespaces;
777 $this->siteInfoCallback( $siteInfo );
780 private function handleLogItem() {
781 $this->
debug(
"Enter log item handler." );
785 $normalFields = [
'id',
'comment',
'type',
'action',
'timestamp',
786 'logtitle',
'params' ];
788 while ( $this->reader->read() ) {
789 if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
790 $this->reader->localName ==
'logitem' ) {
794 $tag = $this->reader->localName;
796 if ( !$this->hookRunner->onImportHandleLogItemXMLTag( $this, $logInfo ) ) {
798 } elseif ( in_array( $tag, $normalFields ) ) {
800 } elseif ( $tag ==
'contributor' ) {
801 $logInfo[
'contributor'] = $this->handleContributor();
802 } elseif ( $tag !=
'#text' ) {
803 $this->
warn(
"Unhandled log-item XML tag $tag" );
807 $this->processLogItem( $logInfo );
814 private function processLogItem( $logInfo ) {
817 if ( isset( $logInfo[
'id'] ) ) {
818 $revision->setID( $logInfo[
'id'] );
820 $revision->setType( $logInfo[
'type'] );
821 $revision->setAction( $logInfo[
'action'] );
822 if ( isset( $logInfo[
'timestamp'] ) ) {
823 $revision->setTimestamp( $logInfo[
'timestamp'] );
825 if ( isset( $logInfo[
'params'] ) ) {
826 $revision->setParams( $logInfo[
'params'] );
828 if ( isset( $logInfo[
'logtitle'] ) ) {
831 $revision->setTitle( Title::newFromText( $logInfo[
'logtitle'] ) );
834 $revision->setNoUpdates( $this->mNoUpdates );
836 if ( isset( $logInfo[
'comment'] ) ) {
837 $revision->setComment( $logInfo[
'comment'] );
840 if ( isset( $logInfo[
'contributor'][
'username'] ) ) {
841 $revision->setUsername(
842 $this->externalUserNames->applyPrefix( $logInfo[
'contributor'][
'username'] )
844 } elseif ( isset( $logInfo[
'contributor'][
'ip'] ) ) {
845 $revision->setUserIP( $logInfo[
'contributor'][
'ip'] );
847 $revision->setUsername( $this->externalUserNames->addPrefix(
'Unknown user' ) );
850 return $this->logItemCallback( $revision );
853 private function handlePage() {
855 $this->
debug(
"Enter page handler." );
856 $pageInfo = [
'revisionCount' => 0,
'successfulRevisionCount' => 0 ];
859 $normalFields = [
'title',
'ns',
'id',
'redirect',
'restrictions' ];
864 while ( $skip ? $this->reader->next() : $this->reader->read() ) {
865 if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
866 $this->reader->localName ==
'page' ) {
872 $tag = $this->reader->localName;
877 } elseif ( !$this->hookRunner->onImportHandlePageXMLTag( $this, $pageInfo ) ) {
879 } elseif ( in_array( $tag, $normalFields ) ) {
887 if ( $tag ==
'redirect' ) {
892 } elseif ( $tag ==
'revision' || $tag ==
'upload' ) {
893 if ( !isset( $title ) ) {
894 $title = $this->processTitle( $pageInfo[
'title'],
895 $pageInfo[
'ns'] ??
null );
898 if ( is_array( $title ) ) {
900 [ $pageInfo[
'_title'], $foreignTitle ] = $title;
908 if ( $tag ==
'revision' ) {
909 $this->handleRevision( $pageInfo );
911 $this->handleUpload( $pageInfo );
914 } elseif ( $tag !=
'#text' ) {
915 $this->
warn(
"Unhandled page XML tag $tag" );
925 if ( array_key_exists(
'_title', $pageInfo ) ) {
927 $title = $pageInfo[
'_title'];
928 $this->pageOutCallback(
932 $pageInfo[
'revisionCount'],
933 $pageInfo[
'successfulRevisionCount'],
942 private function handleRevision( &$pageInfo ) {
943 $this->
debug(
"Enter revision handler" );
946 $normalFields = [
'id',
'parentid',
'timestamp',
'comment',
'minor',
'origin',
947 'model',
'format',
'text',
'sha1' ];
951 while ( $skip ? $this->reader->next() : $this->reader->read() ) {
952 if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
953 $this->reader->localName ==
'revision' ) {
957 $tag = $this->reader->localName;
959 if ( !$this->hookRunner->onImportHandleRevisionXMLTag(
960 $this, $pageInfo, $revisionInfo )
963 } elseif ( in_array( $tag, $normalFields ) ) {
965 } elseif ( $tag ==
'content' ) {
967 $revisionInfo[$tag][] = $this->handleContent();
968 } elseif ( $tag ==
'contributor' ) {
969 $revisionInfo[
'contributor'] = $this->handleContributor();
970 } elseif ( $tag !=
'#text' ) {
971 $this->
warn(
"Unhandled revision XML tag $tag" );
976 $pageInfo[
'revisionCount']++;
977 if ( $this->processRevision( $pageInfo, $revisionInfo ) ) {
978 $pageInfo[
'successfulRevisionCount']++;
982 private function handleContent(): array {
983 $this->
debug(
"Enter content handler" );
986 $normalFields = [
'role',
'origin',
'model',
'format',
'text' ];
990 while ( $skip ? $this->reader->next() : $this->reader->read() ) {
991 if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
992 $this->reader->localName ==
'content' ) {
996 $tag = $this->reader->localName;
998 if ( !$this->hookRunner->onImportHandleContentXMLTag(
999 $this, $contentInfo )
1002 } elseif ( in_array( $tag, $normalFields ) ) {
1004 } elseif ( $tag !=
'#text' ) {
1005 $this->
warn(
"Unhandled content XML tag $tag" );
1010 return $contentInfo;
1020 private function makeContent(
PageIdentity $page, $revisionId, $contentInfo ) {
1021 $maxArticleSize = $this->config->get( MainConfigNames::MaxArticleSize );
1023 if ( !isset( $contentInfo[
'text'] ) ) {
1024 throw new InvalidArgumentException(
'Missing text field in import.' );
1031 if ( ( !isset( $contentInfo[
'model'] ) ||
1032 in_array( $contentInfo[
'model'], [
1040 strlen( $contentInfo[
'text'] ) > $maxArticleSize * 1024
1042 throw new RuntimeException(
'The text of ' .
1044 "the revision with ID $revisionId" :
1046 ) .
" exceeds the maximum allowable size ({$maxArticleSize} KiB)" );
1049 $role = $contentInfo[
'role'] ?? SlotRecord::MAIN;
1050 $model = $contentInfo[
'model'] ?? $this->slotRoleRegistry
1051 ->getRoleHandler( $role )
1052 ->getDefaultModel( $page );
1053 $handler = $this->contentHandlerFactory->getContentHandler( $model );
1055 $text = $handler->importTransform( $contentInfo[
'text'] );
1057 return $handler->unserializeContent( $text );
1065 private function processRevision( $pageInfo, $revisionInfo ) {
1068 $revId = $revisionInfo[
'id'] ?? 0;
1070 $revision->setID( $revisionInfo[
'id'] );
1073 $title = $pageInfo[
'_title'];
1074 $revision->setTitle( $title );
1076 $content = $this->makeContent( $title, $revId, $revisionInfo );
1077 $revision->setContent( SlotRecord::MAIN, $content );
1079 foreach ( $revisionInfo[
'content'] ?? [] as $slotInfo ) {
1080 if ( !isset( $slotInfo[
'role'] ) ) {
1081 throw new RuntimeException(
"Missing role for imported slot." );
1084 $content = $this->makeContent( $title, $revId, $slotInfo );
1085 $revision->setContent( $slotInfo[
'role'], $content );
1087 $revision->setTimestamp( $revisionInfo[
'timestamp'] ??
wfTimestampNow() );
1089 if ( isset( $revisionInfo[
'comment'] ) ) {
1090 $revision->setComment( $revisionInfo[
'comment'] );
1093 if ( isset( $revisionInfo[
'minor'] ) ) {
1094 $revision->setMinor(
true );
1096 if ( isset( $revisionInfo[
'contributor'][
'username'] ) ) {
1097 $revision->setUsername(
1098 $this->externalUserNames->applyPrefix( $revisionInfo[
'contributor'][
'username'] )
1100 } elseif ( isset( $revisionInfo[
'contributor'][
'ip'] ) ) {
1101 $revision->setUserIP( $revisionInfo[
'contributor'][
'ip'] );
1103 $revision->setUsername( $this->externalUserNames->addPrefix(
'Unknown user' ) );
1105 if ( isset( $revisionInfo[
'sha1'] ) ) {
1106 $revision->setSha1Base36( $revisionInfo[
'sha1'] );
1108 $revision->setNoUpdates( $this->mNoUpdates );
1110 return $this->revisionCallback( $revision );
1117 private function handleUpload( &$pageInfo ) {
1118 $this->debug(
"Enter upload handler" );
1121 $normalFields = [
'timestamp',
'comment',
'filename',
'text',
1122 'src',
'size',
'sha1base36',
'archivename',
'rel' ];
1126 while ( $skip ? $this->reader->next() : $this->reader->read() ) {
1127 if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
1128 $this->reader->localName ==
'upload' ) {
1132 $tag = $this->reader->localName;
1134 if ( !$this->hookRunner->onImportHandleUploadXMLTag( $this, $pageInfo ) ) {
1136 } elseif ( in_array( $tag, $normalFields ) ) {
1137 $uploadInfo[$tag] = $this->nodeContents();
1138 } elseif ( $tag ==
'contributor' ) {
1139 $uploadInfo[
'contributor'] = $this->handleContributor();
1140 } elseif ( $tag ==
'contents' ) {
1141 $contents = $this->nodeContents();
1142 $encoding = $this->reader->getAttribute(
'encoding' );
1143 if ( $encoding ===
'base64' ) {
1144 $uploadInfo[
'fileSrc'] = $this->dumpTemp( base64_decode( $contents ) );
1145 $uploadInfo[
'isTempSrc'] =
true;
1147 } elseif ( $tag !=
'#text' ) {
1148 $this->warn(
"Unhandled upload XML tag $tag" );
1153 if ( $this->mImageBasePath && isset( $uploadInfo[
'rel'] ) ) {
1154 $path =
"{$this->mImageBasePath}/{$uploadInfo['rel']}";
1155 if ( file_exists(
$path ) ) {
1156 $uploadInfo[
'fileSrc'] =
$path;
1157 $uploadInfo[
'isTempSrc'] =
false;
1161 if ( $this->mImportUploads ) {
1162 return $this->processUpload( $pageInfo, $uploadInfo );
1170 private function dumpTemp( $contents ) {
1171 $filename = tempnam(
wfTempDir(),
'importupload' );
1172 file_put_contents( $filename, $contents );
1181 private function processUpload( $pageInfo, $uploadInfo ) {
1183 $revId = $pageInfo[
'id'];
1184 $title = $pageInfo[
'_title'];
1186 $uploadInfo[
'text'] ??=
'';
1187 $content = $this->makeContent( $title, $revId, $uploadInfo );
1189 $revision->setTitle( $title );
1190 $revision->setID( $revId );
1191 $revision->setTimestamp( $uploadInfo[
'timestamp'] );
1192 $revision->setContent( SlotRecord::MAIN, $content );
1193 $revision->setFilename( $uploadInfo[
'filename'] );
1194 if ( isset( $uploadInfo[
'archivename'] ) ) {
1195 $revision->setArchiveName( $uploadInfo[
'archivename'] );
1197 $revision->setSrc( $uploadInfo[
'src'] );
1198 if ( isset( $uploadInfo[
'fileSrc'] ) ) {
1199 $revision->setFileSrc( $uploadInfo[
'fileSrc'],
1200 !empty( $uploadInfo[
'isTempSrc'] )
1203 if ( isset( $uploadInfo[
'sha1base36'] ) ) {
1204 $revision->setSha1Base36( $uploadInfo[
'sha1base36'] );
1206 $revision->setSize( intval( $uploadInfo[
'size'] ) );
1207 $revision->setComment( $uploadInfo[
'comment'] );
1209 if ( isset( $uploadInfo[
'contributor'][
'username'] ) ) {
1210 $revision->setUsername(
1211 $this->externalUserNames->applyPrefix( $uploadInfo[
'contributor'][
'username'] )
1213 } elseif ( isset( $uploadInfo[
'contributor'][
'ip'] ) ) {
1214 $revision->setUserIP( $uploadInfo[
'contributor'][
'ip'] );
1216 $revision->setNoUpdates( $this->mNoUpdates );
1218 return ( $this->mUploadCallback )( $revision );
1224 private function handleContributor() {
1225 $this->debug(
"Enter contributor handler." );
1227 if ( $this->reader->isEmptyElement ) {
1231 $fields = [
'id',
'ip',
'username' ];
1234 while ( $this->reader->read() ) {
1235 if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
1236 $this->reader->localName ==
'contributor' ) {
1240 $tag = $this->reader->localName;
1242 if ( in_array( $tag, $fields ) ) {
1243 $info[$tag] = $this->nodeContents();
1255 private function processTitle( $text, $ns =
null ) {
1256 if ( $this->foreignNamespaces ===
null ) {
1258 $this->contentLanguage
1262 $this->foreignNamespaces );
1265 $foreignTitle = $foreignTitleFactory->createForeignTitle( $text,
1268 $title = $this->importTitleFactory->createTitleFromForeignTitle(
1271 if ( $title ===
null ) {
1272 # Invalid page title? Ignore the page
1273 $this->notice(
'import-error-invalid', $foreignTitle->getFullText() );
1275 } elseif ( $title->isExternal() ) {
1276 $this->notice(
'import-error-interwiki', $title->getPrefixedText() );
1278 } elseif ( !$title->canExist() ) {
1279 $this->notice(
'import-error-special', $title->getPrefixedText() );
1281 } elseif ( !$this->performer->definitelyCan(
'edit', $title ) ) {
1282 # Do not import if the importing wiki user cannot edit this page
1283 $this->notice(
'import-error-edit', $title->getPrefixedText() );
1287 return [ $title, $foreignTitle ];
1294 private function openReader() {
1298 $oldDisable = @libxml_disable_entity_loader(
false );
1300 if ( PHP_VERSION_ID >= 80000 ) {
1302 $reader = XMLReader::open(
1303 'uploadsource://' . $this->sourceAdapterId,
null, LIBXML_PARSEHUGE );
1304 if ( $reader instanceof XMLReader ) {
1305 $this->reader = $reader;
1312 $this->reader =
new XMLReader;
1313 $status = $this->reader->open(
1314 'uploadsource://' . $this->sourceAdapterId,
null, LIBXML_PARSEHUGE );
1317 $error = libxml_get_last_error();
1319 @libxml_disable_entity_loader( $oldDisable );
1320 throw new RuntimeException(
1321 'Encountered an internal error while initializing WikiImporter object: ' . $error->message
1325 @libxml_disable_entity_loader( $oldDisable );
1331 private function syntaxCheckXML() {
1335 AtEase::suppressWarnings();
1336 $oldDisable = libxml_disable_entity_loader(
false );
1338 while ( $this->reader->read() );
1339 $error = libxml_get_last_error();
1341 $errorMessage =
'XML error at line ' . $error->line .
': ' . $error->message;
1342 wfDebug( __METHOD__ .
': Invalid xml found - ' . $errorMessage );
1343 throw new RuntimeException( $errorMessage );
1346 libxml_disable_entity_loader( $oldDisable );
1347 AtEase::restoreWarnings();
1348 $this->reader->close();
1353 $this->openReader();