MediaWiki  master
XmlDumpWriter.php
Go to the documentation of this file.
1 <?php
32 
37 
39  const WRITE_CONTENT = 0;
40 
42  const WRITE_STUB = 1;
43 
48  const WRITE_STUB_DELETED = 2;
49 
54  public static $supportedSchemas = [
57  ];
58 
64  private $schemaVersion;
65 
71  private $currentTitle = null;
72 
76  private $contentMode;
77 
86  public function __construct(
87  $contentMode = self::WRITE_CONTENT,
89  ) {
90  Assert::parameter(
91  in_array( $contentMode, [ self::WRITE_CONTENT, self::WRITE_STUB ] ),
92  '$contentMode',
93  'must be one of the following constants: WRITE_CONTENT or WRITE_STUB.'
94  );
95 
96  Assert::parameter(
97  in_array( $schemaVersion, self::$supportedSchemas ),
98  '$schemaVersion',
99  'must be one of the following schema versions: '
100  . implode( ',', self::$supportedSchemas )
101  );
102 
103  $this->contentMode = $contentMode;
104  $this->schemaVersion = $schemaVersion;
105  }
106 
117  function openStream() {
118  $ver = $this->schemaVersion;
119  return Xml::element( 'mediawiki', [
120  'xmlns' => "http://www.mediawiki.org/xml/export-$ver/",
121  'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
122  /*
123  * When a new version of the schema is created, it needs staging on mediawiki.org.
124  * This requires a change in the operations/mediawiki-config git repo.
125  *
126  * Create a changeset like https://gerrit.wikimedia.org/r/#/c/149643/ in which
127  * you copy in the new xsd file.
128  *
129  * After it is reviewed, merged and deployed (sync-docroot), the index.html needs purging.
130  * echo "https://www.mediawiki.org/xml/index.html" | mwscript purgeList.php --wiki=aawiki
131  */
132  'xsi:schemaLocation' => "http://www.mediawiki.org/xml/export-$ver/ " .
133  "http://www.mediawiki.org/xml/export-$ver.xsd",
134  'version' => $ver,
135  'xml:lang' => MediaWikiServices::getInstance()->getContentLanguage()->getHtmlCode() ],
136  null ) .
137  "\n" .
138  $this->siteInfo();
139  }
140 
144  function siteInfo() {
145  $info = [
146  $this->sitename(),
147  $this->dbname(),
148  $this->homelink(),
149  $this->generator(),
150  $this->caseSetting(),
151  $this->namespaces() ];
152  return " <siteinfo>\n " .
153  implode( "\n ", $info ) .
154  "\n </siteinfo>\n";
155  }
156 
160  function sitename() {
161  global $wgSitename;
162  return Xml::element( 'sitename', [], $wgSitename );
163  }
164 
168  function dbname() {
169  global $wgDBname;
170  return Xml::element( 'dbname', [], $wgDBname );
171  }
172 
176  function generator() {
177  global $wgVersion;
178  return Xml::element( 'generator', [], "MediaWiki $wgVersion" );
179  }
180 
184  function homelink() {
185  return Xml::element( 'base', [], Title::newMainPage()->getCanonicalURL() );
186  }
187 
191  function caseSetting() {
192  global $wgCapitalLinks;
193  // "case-insensitive" option is reserved for future
194  $sensitivity = $wgCapitalLinks ? 'first-letter' : 'case-sensitive';
195  return Xml::element( 'case', [], $sensitivity );
196  }
197 
201  function namespaces() {
202  $spaces = "<namespaces>\n";
203  $nsInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
204  foreach (
205  MediaWikiServices::getInstance()->getContentLanguage()->getFormattedNamespaces()
206  as $ns => $title
207  ) {
208  $spaces .= ' ' .
209  Xml::element( 'namespace',
210  [
211  'key' => $ns,
212  'case' => $nsInfo->isCapitalized( $ns )
213  ? 'first-letter' : 'case-sensitive',
214  ], $title ) . "\n";
215  }
216  $spaces .= " </namespaces>";
217  return $spaces;
218  }
219 
226  function closeStream() {
227  return "</mediawiki>\n";
228  }
229 
237  public function openPage( $row ) {
238  $out = " <page>\n";
239  $this->currentTitle = Title::newFromRow( $row );
240  $canonicalTitle = self::canonicalTitle( $this->currentTitle );
241  $out .= ' ' . Xml::elementClean( 'title', [], $canonicalTitle ) . "\n";
242  $out .= ' ' . Xml::element( 'ns', [], strval( $row->page_namespace ) ) . "\n";
243  $out .= ' ' . Xml::element( 'id', [], strval( $row->page_id ) ) . "\n";
244  if ( $row->page_is_redirect ) {
245  $page = WikiPage::factory( $this->currentTitle );
246  $redirect = $page->getRedirectTarget();
247  if ( $redirect instanceof Title && $redirect->isValidRedirectTarget() ) {
248  $out .= ' ';
249  $out .= Xml::element( 'redirect', [ 'title' => self::canonicalTitle( $redirect ) ] );
250  $out .= "\n";
251  }
252  }
253 
254  if ( $row->page_restrictions != '' ) {
255  $out .= ' ' . Xml::element( 'restrictions', [],
256  strval( $row->page_restrictions ) ) . "\n";
257  }
258 
259  Hooks::run( 'XmlDumpWriterOpenPage', [ $this, &$out, $row, $this->currentTitle ] );
260 
261  return $out;
262  }
263 
270  function closePage() {
271  if ( $this->currentTitle !== null ) {
272  $linkCache = MediaWikiServices::getInstance()->getLinkCache();
273  // In rare cases, link cache has the same key for some pages which
274  // might be read as part of the same batch. T220424 and T220316
275  $linkCache->clearLink( $this->currentTitle );
276  }
277  return " </page>\n";
278  }
279 
283  private function getRevisionStore() {
284  return MediaWikiServices::getInstance()->getRevisionStore();
285  }
286 
290  private function getBlobStore() {
291  return MediaWikiServices::getInstance()->getBlobStore();
292  }
293 
307  private function invokeLenient( $obj, $method, $warning, $args = [] ) {
308  try {
309  return call_user_func_array( [ $obj, $method ], $args );
310  } catch ( SuppressedDataException $ex ) {
311  return null;
312  } catch ( Exception $ex ) {
313  if ( $ex instanceof MWException || $ex instanceof RuntimeException ||
314  $ex instanceof InvalidArgumentException ) {
315  MWDebug::warning( $warning . ': ' . $ex->getMessage() );
316  return null;
317  } else {
318  throw $ex;
319  }
320  }
321  }
322 
335  function writeRevision( $row, $slotRows = null ) {
336  $rev = $this->getRevisionStore()->newRevisionFromRowAndSlots(
337  $row,
338  $slotRows,
339  0,
340  $this->currentTitle
341  );
342 
343  $out = " <revision>\n";
344  $out .= " " . Xml::element( 'id', null, strval( $rev->getId() ) ) . "\n";
345 
346  if ( $rev->getParentId() ) {
347  $out .= " " . Xml::element( 'parentid', null, strval( $rev->getParentId() ) ) . "\n";
348  }
349 
350  $out .= $this->writeTimestamp( $rev->getTimestamp() );
351 
352  if ( $rev->isDeleted( RevisionRecord::DELETED_USER ) ) {
353  $out .= " " . Xml::element( 'contributor', [ 'deleted' => 'deleted' ] ) . "\n";
354  } else {
355  // empty values get written out as uid 0, see T224221
356  $user = $rev->getUser();
357  $out .= $this->writeContributor(
358  $user ? $user->getId() : 0,
359  $user ? $user->getName() : ''
360  );
361  }
362 
363  if ( $rev->isMinor() ) {
364  $out .= " <minor/>\n";
365  }
366  if ( $rev->isDeleted( RevisionRecord::DELETED_COMMENT ) ) {
367  $out .= " " . Xml::element( 'comment', [ 'deleted' => 'deleted' ] ) . "\n";
368  } else {
369  if ( $rev->getComment()->text != '' ) {
370  $out .= " "
371  . Xml::elementClean( 'comment', [], strval( $rev->getComment()->text ) )
372  . "\n";
373  }
374  }
375 
376  $contentMode = $rev->isDeleted( RevisionRecord::DELETED_TEXT ) ? self::WRITE_STUB_DELETED
378 
379  foreach ( $rev->getSlots()->getSlots() as $slot ) {
380  $out .= $this->writeSlot( $slot, $contentMode );
381  }
382 
383  if ( $rev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
384  $out .= " <sha1/>\n";
385  } else {
386  $sha1 = $this->invokeLenient(
387  $rev,
388  'getSha1',
389  'failed to determine sha1 for revision ' . $rev->getId()
390  );
391  $out .= " " . Xml::element( 'sha1', null, strval( $sha1 ) ) . "\n";
392  }
393 
394  // Avoid PHP 7.1 warning from passing $this by reference
395  $writer = $this;
396  $text = '';
397  if ( $contentMode === self::WRITE_CONTENT ) {
399  $content = $this->invokeLenient(
400  $rev,
401  'getContent',
402  'Failed to load main slot content of revision ' . $rev->getId(),
403  [ SlotRecord::MAIN, RevisionRecord::RAW ]
404  );
405 
406  $text = $content ? $content->serialize() : '';
407  }
408  Hooks::run( 'XmlDumpWriterWriteRevision', [ &$writer, &$out, $row, $text, $rev ] );
409 
410  $out .= " </revision>\n";
411 
412  return $out;
413  }
414 
421  private function writeSlot( SlotRecord $slot, $contentMode ) {
422  $isMain = $slot->getRole() === SlotRecord::MAIN;
423  $isV11 = $this->schemaVersion >= XML_DUMP_SCHEMA_VERSION_11;
424 
425  if ( !$isV11 && !$isMain ) {
426  // ignore extra slots
427  return '';
428  }
429 
430  $out = '';
431  $indent = ' ';
432 
433  if ( !$isMain ) {
434  // non-main slots are wrapped into an additional element.
435  $out .= ' ' . Xml::openElement( 'content' ) . "\n";
436  $indent .= ' ';
437  $out .= $indent . Xml::element( 'role', null, strval( $slot->getRole() ) ) . "\n";
438  }
439 
440  if ( $isV11 ) {
441  $out .= $indent . Xml::element( 'origin', null, strval( $slot->getOrigin() ) ) . "\n";
442  }
443 
444  $contentModel = $slot->getModel();
445  $contentHandler = ContentHandler::getForModelID( $contentModel );
446  $contentFormat = $contentHandler->getDefaultFormat();
447 
448  // XXX: The content format is only relevant when actually outputting serialized content.
449  // It should probably be an attribute on the text tag.
450  $out .= $indent . Xml::element( 'model', null, strval( $contentModel ) ) . "\n";
451  $out .= $indent . Xml::element( 'format', null, strval( $contentFormat ) ) . "\n";
452 
453  $textAttributes = [
454  'xml:space' => 'preserve',
455  'bytes' => $this->invokeLenient(
456  $slot,
457  'getSize',
458  'failed to determine size for slot ' . $slot->getRole() . ' of revision '
459  . $slot->getRevision()
460  ) ?: '0'
461  ];
462 
463  if ( $isV11 ) {
464  $textAttributes['sha1'] = $this->invokeLenient(
465  $slot,
466  'getSha1',
467  'failed to determine sha1 for slot ' . $slot->getRole() . ' of revision '
468  . $slot->getRevision()
469  ) ?: '';
470  }
471 
472  if ( $contentMode === self::WRITE_CONTENT ) {
473  $content = $this->invokeLenient(
474  $slot,
475  'getContent',
476  'failed to load content for slot ' . $slot->getRole() . ' of revision '
477  . $slot->getRevision()
478  );
479 
480  if ( $content === null ) {
481  $out .= $indent . Xml::element( 'text', $textAttributes ) . "\n";
482  } else {
483  $out .= $this->writeText( $content, $textAttributes, $indent );
484  }
485  } elseif ( $contentMode === self::WRITE_STUB_DELETED ) {
486  // write <text> placeholder tag
487  $textAttributes['deleted'] = 'deleted';
488  $out .= $indent . Xml::element( 'text', $textAttributes ) . "\n";
489  } else {
490  // write <text> stub tag
491  if ( $isV11 ) {
492  $textAttributes['location'] = $slot->getAddress();
493  }
494 
495  // Output the numerical text ID if possible, for backwards compatibility.
496  // Note that this is currently the ONLY reason we have a BlobStore here at all.
497  // When removing this line, check whether the BlobStore has become unused.
498  try {
499  // NOTE: this will only work for addresses of the form "tt:12345".
500  // If we want to support other kinds of addresses in the future,
501  // we will have to silently ignore failures here.
502  // For now, this fails for "tt:0", which is present in the WMF production
503  // database of of Juli 2019, due to data corruption.
504  $textId = $this->getBlobStore()->getTextIdFromAddress( $slot->getAddress() );
505  } catch ( InvalidArgumentException $ex ) {
506  MWDebug::warning( 'Bad content address for slot ' . $slot->getRole()
507  . ' of revision ' . $slot->getRevision() . ': ' . $ex->getMessage() );
508  $textId = 0;
509  }
510 
511  if ( $textId ) {
512  $textAttributes['id'] = $textId;
513  }
514 
515  $out .= $indent . Xml::element( 'text', $textAttributes ) . "\n";
516  }
517 
518  if ( !$isMain ) {
519  $out .= ' ' . Xml::closeElement( 'content' ) . "\n";
520  }
521 
522  return $out;
523  }
524 
532  private function writeText( Content $content, $textAttributes, $indent ) {
533  $out = '';
534 
535  $contentHandler = $content->getContentHandler();
536  $contentFormat = $contentHandler->getDefaultFormat();
537 
538  if ( $content instanceof TextContent ) {
539  // HACK: For text based models, bypass the serialization step. This allows extensions (like Flow)
540  // that use incompatible combinations of serialization format and content model.
541  $data = $content->getNativeData();
542  } else {
543  $data = $content->serialize( $contentFormat );
544  }
545 
546  $data = $contentHandler->exportTransform( $data, $contentFormat );
547  $textAttributes['bytes'] = $size = strlen( $data ); // make sure to use the actual size
548  $out .= $indent . Xml::elementClean( 'text', $textAttributes, strval( $data ) ) . "\n";
549 
550  return $out;
551  }
552 
561  function writeLogItem( $row ) {
562  $out = " <logitem>\n";
563  $out .= " " . Xml::element( 'id', null, strval( $row->log_id ) ) . "\n";
564 
565  $out .= $this->writeTimestamp( $row->log_timestamp, " " );
566 
567  if ( $row->log_deleted & LogPage::DELETED_USER ) {
568  $out .= " " . Xml::element( 'contributor', [ 'deleted' => 'deleted' ] ) . "\n";
569  } else {
570  $out .= $this->writeContributor( $row->log_user, $row->user_name, " " );
571  }
572 
573  if ( $row->log_deleted & LogPage::DELETED_COMMENT ) {
574  $out .= " " . Xml::element( 'comment', [ 'deleted' => 'deleted' ] ) . "\n";
575  } else {
576  $comment = CommentStore::getStore()->getComment( 'log_comment', $row )->text;
577  if ( $comment != '' ) {
578  $out .= " " . Xml::elementClean( 'comment', null, strval( $comment ) ) . "\n";
579  }
580  }
581 
582  $out .= " " . Xml::element( 'type', null, strval( $row->log_type ) ) . "\n";
583  $out .= " " . Xml::element( 'action', null, strval( $row->log_action ) ) . "\n";
584 
585  if ( $row->log_deleted & LogPage::DELETED_ACTION ) {
586  $out .= " " . Xml::element( 'text', [ 'deleted' => 'deleted' ] ) . "\n";
587  } else {
588  $title = Title::makeTitle( $row->log_namespace, $row->log_title );
589  $out .= " " . Xml::elementClean( 'logtitle', null, self::canonicalTitle( $title ) ) . "\n";
590  $out .= " " . Xml::elementClean( 'params',
591  [ 'xml:space' => 'preserve' ],
592  strval( $row->log_params ) ) . "\n";
593  }
594 
595  $out .= " </logitem>\n";
596 
597  return $out;
598  }
599 
605  function writeTimestamp( $timestamp, $indent = " " ) {
606  $ts = wfTimestamp( TS_ISO_8601, $timestamp );
607  return $indent . Xml::element( 'timestamp', null, $ts ) . "\n";
608  }
609 
616  function writeContributor( $id, $text, $indent = " " ) {
617  $out = $indent . "<contributor>\n";
618  if ( $id || !IP::isValid( $text ) ) {
619  $out .= $indent . " " . Xml::elementClean( 'username', null, strval( $text ) ) . "\n";
620  $out .= $indent . " " . Xml::element( 'id', null, strval( $id ) ) . "\n";
621  } else {
622  $out .= $indent . " " . Xml::elementClean( 'ip', null, strval( $text ) ) . "\n";
623  }
624  $out .= $indent . "</contributor>\n";
625  return $out;
626  }
627 
634  function writeUploads( $row, $dumpContents = false ) {
635  if ( $row->page_namespace == NS_FILE ) {
636  $img = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()
637  ->newFile( $row->page_title );
638  if ( $img && $img->exists() ) {
639  $out = '';
640  foreach ( array_reverse( $img->getHistory() ) as $ver ) {
641  $out .= $this->writeUpload( $ver, $dumpContents );
642  }
643  $out .= $this->writeUpload( $img, $dumpContents );
644  return $out;
645  }
646  }
647  return '';
648  }
649 
655  function writeUpload( $file, $dumpContents = false ) {
656  if ( $file->isOld() ) {
658  '@phan-var OldLocalFile $file';
659  $archiveName = " " .
660  Xml::element( 'archivename', null, $file->getArchiveName() ) . "\n";
661  } else {
662  $archiveName = '';
663  }
664  if ( $dumpContents ) {
665  $be = $file->getRepo()->getBackend();
666  # Dump file as base64
667  # Uses only XML-safe characters, so does not need escaping
668  # @todo Too bad this loads the contents into memory (script might swap)
669  $contents = ' <contents encoding="base64">' .
670  chunk_split( base64_encode(
671  $be->getFileContents( [ 'src' => $file->getPath() ] ) ) ) .
672  " </contents>\n";
673  } else {
674  $contents = '';
675  }
676  if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
677  $comment = Xml::element( 'comment', [ 'deleted' => 'deleted' ] );
678  } else {
679  $comment = Xml::elementClean( 'comment', null, strval( $file->getDescription() ) );
680  }
681  return " <upload>\n" .
682  $this->writeTimestamp( $file->getTimestamp() ) .
683  $this->writeContributor( $file->getUser( 'id' ), $file->getUser( 'text' ) ) .
684  " " . $comment . "\n" .
685  " " . Xml::element( 'filename', null, $file->getName() ) . "\n" .
686  $archiveName .
687  " " . Xml::element( 'src', null, $file->getCanonicalUrl() ) . "\n" .
688  " " . Xml::element( 'size', null, $file->getSize() ) . "\n" .
689  " " . Xml::element( 'sha1base36', null, $file->getSha1() ) . "\n" .
690  " " . Xml::element( 'rel', null, $file->getRel() ) . "\n" .
691  $contents .
692  " </upload>\n";
693  }
694 
705  public static function canonicalTitle( Title $title ) {
706  if ( $title->isExternal() ) {
707  return $title->getPrefixedText();
708  }
709 
710  $prefix = MediaWikiServices::getInstance()->getContentLanguage()->
711  getFormattedNsText( $title->getNamespace() );
712 
713  // @todo Emit some kind of warning to the user if $title->getNamespace() !==
714  // NS_MAIN and $prefix === '' (viz. pages in an unregistered namespace)
715 
716  if ( $prefix !== '' ) {
717  $prefix .= ':';
718  }
719 
720  return $prefix . $title->getText();
721  }
722 }
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
Definition: WikiPage.php:142
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42
const DELETED_COMMENT
Definition: File.php:64
$wgVersion
MediaWiki version number.
getContentHandler()
Convenience method that returns the ContentHandler singleton for handling the content model that this...
$wgSitename
Name of the site.
getText()
Get the text form (spaces not underscores) of the main part.
Definition: Title.php:998
static newMainPage(MessageLocalizer $localizer=null)
Create a new Title for the Main Page.
Definition: Title.php:648
const WRITE_CONTENT
Output serialized revision content.
const WRITE_STUB
Only output subs for revision content.
const XML_DUMP_SCHEMA_VERSION_10
Definition: Defines.php:308
closePage()
Closes a "<page>" section on the output stream.
static elementClean( $element, $attribs=[], $contents='')
Format an XML element as with self::element(), but run text through the content language&#39;s normalize(...
Definition: Xml.php:90
invokeLenient( $obj, $method, $warning, $args=[])
Invokes the given method on the given object, catching and logging any storage related exceptions...
getPrefixedText()
Get the prefixed title with spaces.
Definition: Title.php:1858
closeStream()
Closes the output stream with the closing root element.
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:39
static newFromRow( $row)
Make a Title object from a DB row.
Definition: Title.php:518
if( $line===false) $args
Definition: cdb.php:64
getModel()
Returns the content model.
Definition: SlotRecord.php:566
writeText(Content $content, $textAttributes, $indent)
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
const DELETED_COMMENT
Definition: LogPage.php:35
openPage( $row)
Opens a "<page>" section on the output stream, with data from the given database row.
writeUpload( $file, $dumpContents=false)
writeTimestamp( $timestamp, $indent=" ")
isExternal()
Is this Title interwiki?
Definition: Title.php:915
static warning( $msg, $callerOffset=1, $level=E_USER_NOTICE, $log='auto')
Adds a warning entry to the log.
Definition: MWDebug.php:175
static openElement( $element, $attribs=null)
This opens an XML element.
Definition: Xml.php:108
static getForModelID( $modelId)
Returns the ContentHandler singleton for the given model ID.
$wgCapitalLinks
Set this to false to avoid forcing the first letter of links to capitals.
static isValid( $ip)
Validate an IP address.
Definition: IP.php:111
writeUploads( $row, $dumpContents=false)
Warning! This data is potentially inconsistent.
getRevision()
Returns the ID of the revision this slot is associated with.
Definition: SlotRecord.php:396
Exception raised in response to an audience check when attempting to access suppressed information wi...
int $contentMode
Whether to output revision content or just stubs.
writeRevision( $row, $slotRows=null)
Dumps a "<revision>" section on the output stream, with data filled in from the given database row...
openStream()
Opens the XML output stream&#39;s root "<mediawiki>" element.
writeSlot(SlotRecord $slot, $contentMode)
getNamespace()
Get the namespace index, i.e.
Definition: Title.php:1040
const NS_FILE
Definition: Defines.php:66
string $schemaVersion
which schema version the generated XML should comply to.
getOrigin()
Returns the revision ID of the revision that originated the slot&#39;s content.
Definition: SlotRecord.php:405
getNativeData()
Returns native representation of the data.
$wgDBname
Current wiki database name.
const DELETED_USER
Definition: LogPage.php:36
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:586
getRole()
Returns the role of the slot.
Definition: SlotRecord.php:489
writeLogItem( $row)
Dumps a "<logitem>" section on the output stream, with data filled in from the given database row...
static closeElement( $element)
Shortcut to close an XML element.
Definition: Xml.php:117
static getStore()
__construct( $contentMode=self::WRITE_CONTENT, $schemaVersion=XML_DUMP_SCHEMA_VERSION_11)
XmlDumpWriter constructor.
Title null $currentTitle
Title of the currently processed page.
getAddress()
Returns the address of this slot&#39;s content.
Definition: SlotRecord.php:499
static element( $element, $attribs=null, $contents='', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.
Definition: Xml.php:41
static string [] $supportedSchemas
the schema versions supported for output
$content
Definition: router.php:78
const WRITE_STUB_DELETED
Only output subs for revision content, indicating that the content has been deleted/suppressed.
const DELETED_ACTION
Definition: LogPage.php:34
writeContributor( $id, $text, $indent=" ")
const XML_DUMP_SCHEMA_VERSION_11
Definition: Defines.php:309
static canonicalTitle(Title $title)
Return prefixed text form of title, but using the content language&#39;s canonical namespace.
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
serialize( $format=null)
Convenience method for serializing this Content object.