MediaWiki  1.23.14
parserTest.inc
Go to the documentation of this file.
1 <?php
34 class ParserTest {
38  private $color;
39 
43  private $showOutput;
44 
48  private $useTemporaryTables = true;
49 
53  private $databaseSetupDone = false;
54 
59  private $db;
60 
65  private $dbClone;
66 
70  private $oldTablePrefix;
71 
72  private $maxFuzzTestLength = 300;
73  private $fuzzSeed = 0;
74  private $memoryLimit = 50;
75  private $uploadDir = null;
76 
77  public $regex = "";
78  private $savedGlobals = array();
79 
84  public function __construct( $options = array() ) {
85  # Only colorize output if stdout is a terminal.
86  $this->color = !wfIsWindows() && Maintenance::posix_isatty( 1 );
87 
88  if ( isset( $options['color'] ) ) {
89  switch ( $options['color'] ) {
90  case 'no':
91  $this->color = false;
92  break;
93  case 'yes':
94  default:
95  $this->color = true;
96  break;
97  }
98  }
99 
100  $this->term = $this->color
101  ? new AnsiTermColorer()
102  : new DummyTermColorer();
103 
104  $this->showDiffs = !isset( $options['quick'] );
105  $this->showProgress = !isset( $options['quiet'] );
106  $this->showFailure = !(
107  isset( $options['quiet'] )
108  && ( isset( $options['record'] )
109  || isset( $options['compare'] ) ) ); // redundant output
110 
111  $this->showOutput = isset( $options['show-output'] );
112 
113  if ( isset( $options['filter'] ) ) {
114  $options['regex'] = $options['filter'];
115  }
116 
117  if ( isset( $options['regex'] ) ) {
118  if ( isset( $options['record'] ) ) {
119  echo "Warning: --record cannot be used with --regex, disabling --record\n";
120  unset( $options['record'] );
121  }
122  $this->regex = $options['regex'];
123  } else {
124  # Matches anything
125  $this->regex = '';
126  }
127 
128  $this->setupRecorder( $options );
129  $this->keepUploads = isset( $options['keep-uploads'] );
130 
131  if ( isset( $options['seed'] ) ) {
132  $this->fuzzSeed = intval( $options['seed'] ) - 1;
133  }
134 
135  $this->runDisabled = isset( $options['run-disabled'] );
136  $this->runParsoid = isset( $options['run-parsoid'] );
137 
138  $this->hooks = array();
139  $this->functionHooks = array();
140  self::setUp();
141  }
142 
143  static function setUp() {
144  global $wgParser, $wgParserConf, $IP, $messageMemc, $wgMemc,
145  $wgUser, $wgLang, $wgOut, $wgRequest, $wgStyleDirectory, $wgEnableParserCache,
146  $wgExtraNamespaces, $wgNamespaceAliases, $wgNamespaceProtection, $wgLocalFileRepo,
147  $parserMemc, $wgThumbnailScriptPath, $wgScriptPath,
148  $wgArticlePath, $wgScript, $wgStylePath, $wgExtensionAssetsPath,
149  $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType, $wgLockManagers;
150 
151  $wgScript = '/index.php';
152  $wgScriptPath = '/';
153  $wgArticlePath = '/wiki/$1';
154  $wgStylePath = '/skins';
155  $wgExtensionAssetsPath = '/extensions';
156  $wgThumbnailScriptPath = false;
158  'name' => 'fsLockManager',
159  'class' => 'FSLockManager',
160  'lockDirectory' => wfTempDir() . '/test-repo/lockdir',
161  ), array(
162  'name' => 'nullLockManager',
163  'class' => 'NullLockManager',
164  ) );
165  $wgLocalFileRepo = array(
166  'class' => 'LocalRepo',
167  'name' => 'local',
168  'url' => 'http://example.com/images',
169  'hashLevels' => 2,
170  'transformVia404' => false,
171  'backend' => new FSFileBackend( array(
172  'name' => 'local-backend',
173  'wikiId' => wfWikiId(),
174  'containerPaths' => array(
175  'local-public' => wfTempDir() . '/test-repo/public',
176  'local-thumb' => wfTempDir() . '/test-repo/thumb',
177  'local-temp' => wfTempDir() . '/test-repo/temp',
178  'local-deleted' => wfTempDir() . '/test-repo/deleted',
179  )
180  ) )
181  );
182  $wgNamespaceProtection[NS_MEDIAWIKI] = 'editinterface';
183  $wgNamespaceAliases['Image'] = NS_FILE;
184  $wgNamespaceAliases['Image_talk'] = NS_FILE_TALK;
185  # add a namespace shadowing a interwiki link, to test
186  # proper precedence when resolving links. (bug 51680)
187  $wgExtraNamespaces[100] = 'MemoryAlpha';
188 
189  // XXX: tests won't run without this (for CACHE_DB)
190  if ( $wgMainCacheType === CACHE_DB ) {
192  }
193  if ( $wgMessageCacheType === CACHE_DB ) {
194  $wgMessageCacheType = CACHE_NONE;
195  }
196  if ( $wgParserCacheType === CACHE_DB ) {
197  $wgParserCacheType = CACHE_NONE;
198  }
199 
200  $wgEnableParserCache = false;
202  $wgMemc = wfGetMainCache(); // checks $wgMainCacheType
205 
206  // $wgContLang = new StubContLang;
207  $wgUser = new User;
208  $context = new RequestContext;
209  $wgLang = $context->getLanguage();
210  $wgOut = $context->getOutput();
211  $wgParser = new StubObject( 'wgParser', $wgParserConf['class'], array( $wgParserConf ) );
212  $wgRequest = $context->getRequest();
213 
214  if ( $wgStyleDirectory === false ) {
215  $wgStyleDirectory = "$IP/skins";
216  }
217 
218  self::setupInterwikis();
219  }
220 
230  public static function setupInterwikis() {
231  # Hack: insert a few Wikipedia in-project interwiki prefixes,
232  # for testing inter-language links
233  Hooks::register( 'InterwikiLoadPrefix', function ( $prefix, &$iwData ) {
234  static $testInterwikis = array(
235  'wikipedia' => array(
236  'iw_url' => 'http://en.wikipedia.org/wiki/$1',
237  'iw_api' => '',
238  'iw_wikiid' => '',
239  'iw_local' => 0 ),
240  'meatball' => array(
241  'iw_url' => 'http://www.usemod.com/cgi-bin/mb.pl?$1',
242  'iw_api' => '',
243  'iw_wikiid' => '',
244  'iw_local' => 0 ),
245  'memoryalpha' => array(
246  'iw_url' => 'http://www.memory-alpha.org/en/index.php/$1',
247  'iw_api' => '',
248  'iw_wikiid' => '',
249  'iw_local' => 0 ),
250  'zh' => array(
251  'iw_url' => 'http://zh.wikipedia.org/wiki/$1',
252  'iw_api' => '',
253  'iw_wikiid' => '',
254  'iw_local' => 1 ),
255  'es' => array(
256  'iw_url' => 'http://es.wikipedia.org/wiki/$1',
257  'iw_api' => '',
258  'iw_wikiid' => '',
259  'iw_local' => 1 ),
260  'fr' => array(
261  'iw_url' => 'http://fr.wikipedia.org/wiki/$1',
262  'iw_api' => '',
263  'iw_wikiid' => '',
264  'iw_local' => 1 ),
265  'ru' => array(
266  'iw_url' => 'http://ru.wikipedia.org/wiki/$1',
267  'iw_api' => '',
268  'iw_wikiid' => '',
269  'iw_local' => 1 ),
270  );
271  if ( array_key_exists( $prefix, $testInterwikis ) ) {
272  $iwData = $testInterwikis[$prefix];
273  }
274 
275  // We only want to rely on the above fixtures
276  return false;
277  } );// hooks::register
278  }
279 
283  public static function tearDownInterwikis() {
284  Hooks::clear( 'InterwikiLoadPrefix' );
285  }
286 
287  public function setupRecorder( $options ) {
288  if ( isset( $options['record'] ) ) {
289  $this->recorder = new DbTestRecorder( $this );
290  $this->recorder->version = isset( $options['setversion'] ) ?
291  $options['setversion'] : SpecialVersion::getVersion();
292  } elseif ( isset( $options['compare'] ) ) {
293  $this->recorder = new DbTestPreviewer( $this );
294  } else {
295  $this->recorder = new TestRecorder( $this );
296  }
297  }
298 
303  public static function chomp( $s ) {
304  if ( substr( $s, -1 ) === "\n" ) {
305  return substr( $s, 0, -1 );
306  } else {
307  return $s;
308  }
309  }
310 
315  function fuzzTest( $filenames ) {
316  $GLOBALS['wgContLang'] = Language::factory( 'en' );
317  $dict = $this->getFuzzInput( $filenames );
318  $dictSize = strlen( $dict );
319  $logMaxLength = log( $this->maxFuzzTestLength );
320  $this->setupDatabase();
321  ini_set( 'memory_limit', $this->memoryLimit * 1048576 );
322 
323  $numTotal = 0;
324  $numSuccess = 0;
325  $user = new User;
327  $title = Title::makeTitle( NS_MAIN, 'Parser_test' );
328 
329  while ( true ) {
330  // Generate test input
331  mt_srand( ++$this->fuzzSeed );
332  $totalLength = mt_rand( 1, $this->maxFuzzTestLength );
333  $input = '';
334 
335  while ( strlen( $input ) < $totalLength ) {
336  $logHairLength = mt_rand( 0, 1000000 ) / 1000000 * $logMaxLength;
337  $hairLength = min( intval( exp( $logHairLength ) ), $dictSize );
338  $offset = mt_rand( 0, $dictSize - $hairLength );
339  $input .= substr( $dict, $offset, $hairLength );
340  }
341 
342  $this->setupGlobals();
343  $parser = $this->getParser();
344 
345  // Run the test
346  try {
347  $parser->parse( $input, $title, $opts );
348  $fail = false;
349  } catch ( Exception $exception ) {
350  $fail = true;
351  }
352 
353  if ( $fail ) {
354  echo "Test failed with seed {$this->fuzzSeed}\n";
355  echo "Input:\n";
356  printf( "string(%d) \"%s\"\n\n", strlen( $input ), $input );
357  echo "$exception\n";
358  } else {
359  $numSuccess++;
360  }
361 
362  $numTotal++;
363  $this->teardownGlobals();
364  $parser->__destruct();
365 
366  if ( $numTotal % 100 == 0 ) {
367  $usage = intval( memory_get_usage( true ) / $this->memoryLimit / 1048576 * 100 );
368  echo "{$this->fuzzSeed}: $numSuccess/$numTotal (mem: $usage%)\n";
369  if ( $usage > 90 ) {
370  echo "Out of memory:\n";
371  $memStats = $this->getMemoryBreakdown();
372 
373  foreach ( $memStats as $name => $usage ) {
374  echo "$name: $usage\n";
375  }
376  $this->abort();
377  }
378  }
379  }
380  }
381 
385  function getFuzzInput( $filenames ) {
386  $dict = '';
387 
388  foreach ( $filenames as $filename ) {
389  $contents = file_get_contents( $filename );
390  preg_match_all( '/!!\s*(input|wikitext)\n(.*?)\n!!\s*(result|html|html\/\*|html\/php)/s', $contents, $matches );
391 
392  foreach ( $matches[1] as $match ) {
393  $dict .= $match . "\n";
394  }
395  }
396 
397  return $dict;
398  }
399 
403  function getMemoryBreakdown() {
404  $memStats = array();
405 
406  foreach ( $GLOBALS as $name => $value ) {
407  $memStats['$' . $name] = strlen( serialize( $value ) );
408  }
409 
410  $classes = get_declared_classes();
411 
412  foreach ( $classes as $class ) {
413  $rc = new ReflectionClass( $class );
414  $props = $rc->getStaticProperties();
415  $memStats[$class] = strlen( serialize( $props ) );
416  $methods = $rc->getMethods();
417 
418  foreach ( $methods as $method ) {
419  $memStats[$class] += strlen( serialize( $method->getStaticVariables() ) );
420  }
421  }
422 
423  $functions = get_defined_functions();
424 
425  foreach ( $functions['user'] as $function ) {
426  $rf = new ReflectionFunction( $function );
427  $memStats["$function()"] = strlen( serialize( $rf->getStaticVariables() ) );
428  }
429 
430  asort( $memStats );
431 
432  return $memStats;
433  }
434 
435  function abort() {
436  $this->abort();
437  }
438 
450  public function runTestsFromFiles( $filenames ) {
451  $ok = false;
452 
453  // be sure, ParserTest::addArticle has correct language set,
454  // so that system messages gets into the right language cache
455  $GLOBALS['wgLanguageCode'] = 'en';
456  $GLOBALS['wgContLang'] = Language::factory( 'en' );
457 
458  $this->recorder->start();
459  try {
460  $this->setupDatabase();
461  $ok = true;
462 
463  foreach ( $filenames as $filename ) {
464  $tests = new TestFileIterator( $filename, $this );
465  $ok = $this->runTests( $tests ) && $ok;
466  }
467 
468  $this->teardownDatabase();
469  $this->recorder->report();
470  } catch ( DBError $e ) {
471  echo $e->getMessage();
472  }
473  $this->recorder->end();
474 
475  return $ok;
476  }
477 
478  function runTests( $tests ) {
479  $ok = true;
480 
481  foreach ( $tests as $t ) {
482  $result =
483  $this->runTest( $t['test'], $t['input'], $t['result'], $t['options'], $t['config'] );
484  $ok = $ok && $result;
485  $this->recorder->record( $t['test'], $result );
486  }
487 
488  if ( $this->showProgress ) {
489  print "\n";
490  }
491 
492  return $ok;
493  }
494 
501  function getParser( $preprocessor = null ) {
502  global $wgParserConf;
503 
504  $class = $wgParserConf['class'];
505  $parser = new $class( array( 'preprocessorClass' => $preprocessor ) + $wgParserConf );
506 
507  foreach ( $this->hooks as $tag => $callback ) {
508  $parser->setHook( $tag, $callback );
509  }
510 
511  foreach ( $this->functionHooks as $tag => $bits ) {
512  list( $callback, $flags ) = $bits;
513  $parser->setFunctionHook( $tag, $callback, $flags );
514  }
515 
516  wfRunHooks( 'ParserTestParser', array( &$parser ) );
517 
518  return $parser;
519  }
520 
533  public function runTest( $desc, $input, $result, $opts, $config ) {
534  if ( $this->showProgress ) {
535  $this->showTesting( $desc );
536  }
537 
538  $opts = $this->parseOptions( $opts );
539  $context = $this->setupGlobals( $opts, $config );
540 
541  $user = $context->getUser();
543 
544  if ( isset( $opts['title'] ) ) {
545  $titleText = $opts['title'];
546  } else {
547  $titleText = 'Parser test';
548  }
549 
550  $local = isset( $opts['local'] );
551  $preprocessor = isset( $opts['preprocessor'] ) ? $opts['preprocessor'] : null;
552  $parser = $this->getParser( $preprocessor );
553  $title = Title::newFromText( $titleText );
554 
555  if ( isset( $opts['pst'] ) ) {
556  $out = $parser->preSaveTransform( $input, $title, $user, $options );
557  } elseif ( isset( $opts['msg'] ) ) {
558  $out = $parser->transformMsg( $input, $options, $title );
559  } elseif ( isset( $opts['section'] ) ) {
560  $section = $opts['section'];
561  $out = $parser->getSection( $input, $section );
562  } elseif ( isset( $opts['replace'] ) ) {
563  $section = $opts['replace'][0];
564  $replace = $opts['replace'][1];
565  $out = $parser->replaceSection( $input, $section, $replace );
566  } elseif ( isset( $opts['comment'] ) ) {
567  $out = Linker::formatComment( $input, $title, $local );
568  } elseif ( isset( $opts['preload'] ) ) {
569  $out = $parser->getPreloadText( $input, $title, $options );
570  } else {
571  $output = $parser->parse( $input, $title, $options, true, true, 1337 );
572  $output->setTOCEnabled( !isset( $opts['notoc'] ) );
573  $out = $output->getText();
574 
575  if ( isset( $opts['showtitle'] ) ) {
576  if ( $output->getTitleText() ) {
577  $title = $output->getTitleText();
578  }
579 
580  $out = "$title\n$out";
581  }
582 
583  if ( isset( $opts['ill'] ) ) {
584  $out = $this->tidy( implode( ' ', $output->getLanguageLinks() ) );
585  } elseif ( isset( $opts['cat'] ) ) {
586  $outputPage = $context->getOutput();
587  $outputPage->addCategoryLinks( $output->getCategories() );
588  $cats = $outputPage->getCategoryLinks();
589 
590  if ( isset( $cats['normal'] ) ) {
591  $out = $this->tidy( implode( ' ', $cats['normal'] ) );
592  } else {
593  $out = '';
594  }
595  }
596 
597  $result = $this->tidy( $result );
598  }
599 
600  $this->teardownGlobals();
601 
602  $testResult = new ParserTestResult( $desc );
603  $testResult->expected = $result;
604  $testResult->actual = $out;
605 
606  return $this->showTestResult( $testResult );
607  }
608 
612  function showTestResult( ParserTestResult $testResult ) {
613  if ( $testResult->isSuccess() ) {
614  $this->showSuccess( $testResult );
615  return true;
616  } else {
617  $this->showFailure( $testResult );
618  return false;
619  }
620  }
621 
628  private static function getOptionValue( $key, $opts, $default ) {
629  $key = strtolower( $key );
630 
631  if ( isset( $opts[$key] ) ) {
632  return $opts[$key];
633  } else {
634  return $default;
635  }
636  }
637 
638  private function parseOptions( $instring ) {
639  $opts = array();
640  // foo
641  // foo=bar
642  // foo="bar baz"
643  // foo=[[bar baz]]
644  // foo=bar,"baz quux"
645  // foo={...json...}
646  $defs = '(?(DEFINE)
647  (?<qstr> # Quoted string
648  "
649  (?:[^\\\\"] | \\\\.)*
650  "
651  )
652  (?<json>
653  \{ # Open bracket
654  (?:
655  [^"{}] | # Not a quoted string or object, or
656  (?&qstr) | # A quoted string, or
657  (?&json) # A json object (recursively)
658  )*
659  \} # Close bracket
660  )
661  (?<value>
662  (?:
663  (?&qstr) # Quoted val
664  |
665  \[\[
666  [^]]* # Link target
667  \]\]
668  |
669  [\w-]+ # Plain word
670  |
671  (?&json) # JSON object
672  )
673  )
674  )';
675  $regex = '/' . $defs . '\b
676  (?<k>[\w-]+) # Key
677  \b
678  (?:\s*
679  = # First sub-value
680  \s*
681  (?<v>
682  (?&value)
683  (?:\s*
684  , # Sub-vals 1..N
685  \s*
686  (?&value)
687  )*
688  )
689  )?
690  /x';
691  $valueregex = '/' . $defs . '(?&value)/x';
692 
693  if ( preg_match_all( $regex, $instring, $matches, PREG_SET_ORDER ) ) {
694  foreach ( $matches as $bits ) {
695  $key = strtolower( $bits[ 'k' ] );
696  if ( !isset( $bits[ 'v' ] ) ) {
697  $opts[$key] = true;
698  } else {
699  preg_match_all( $valueregex, $bits[ 'v' ], $vmatches );
700  $opts[$key] = array_map( array( $this, 'cleanupOption' ), $vmatches[0] );
701  if ( count( $opts[$key] ) == 1 ) {
702  $opts[$key] = $opts[$key][0];
703  }
704  }
705  }
706  }
707  return $opts;
708  }
709 
710  private function cleanupOption( $opt ) {
711  if ( substr( $opt, 0, 1 ) == '"' ) {
712  return stripcslashes( substr( $opt, 1, -1 ) );
713  }
714 
715  if ( substr( $opt, 0, 2 ) == '[[' ) {
716  return substr( $opt, 2, -2 );
717  }
718 
719  if ( substr( $opt, 0, 1 ) == '{' ) {
720  return FormatJson::decode( $opt, true );
721  }
722  return $opt;
723  }
724 
729  private function setupGlobals( $opts = '', $config = '' ) {
730  # Find out values for some special options.
731  $lang =
732  self::getOptionValue( 'language', $opts, 'en' );
733  $variant =
734  self::getOptionValue( 'variant', $opts, false );
735  $maxtoclevel =
736  self::getOptionValue( 'wgMaxTocLevel', $opts, 999 );
737  $linkHolderBatchSize =
738  self::getOptionValue( 'wgLinkHolderBatchSize', $opts, 1000 );
739 
740  $settings = array(
741  'wgServer' => 'http://example.org',
742  'wgScript' => '/index.php',
743  'wgScriptPath' => '/',
744  'wgArticlePath' => '/wiki/$1',
745  'wgActionPaths' => array(),
746  'wgLockManagers' => array( array(
747  'name' => 'fsLockManager',
748  'class' => 'FSLockManager',
749  'lockDirectory' => $this->uploadDir . '/lockdir',
750  ), array(
751  'name' => 'nullLockManager',
752  'class' => 'NullLockManager',
753  ) ),
754  'wgLocalFileRepo' => array(
755  'class' => 'LocalRepo',
756  'name' => 'local',
757  'url' => 'http://example.com/images',
758  'hashLevels' => 2,
759  'transformVia404' => false,
760  'backend' => new FSFileBackend( array(
761  'name' => 'local-backend',
762  'wikiId' => wfWikiId(),
763  'containerPaths' => array(
764  'local-public' => $this->uploadDir,
765  'local-thumb' => $this->uploadDir . '/thumb',
766  'local-temp' => $this->uploadDir . '/temp',
767  'local-deleted' => $this->uploadDir . '/delete',
768  )
769  ) )
770  ),
771  'wgEnableUploads' => self::getOptionValue( 'wgEnableUploads', $opts, true ),
772  'wgStylePath' => '/skins',
773  'wgSitename' => 'MediaWiki',
774  'wgLanguageCode' => $lang,
775  'wgDBprefix' => $this->db->getType() != 'oracle' ? 'parsertest_' : 'pt_',
776  'wgRawHtml' => self::getOptionValue( 'wgRawHtml', $opts, false ),
777  'wgLang' => null,
778  'wgContLang' => null,
779  'wgNamespacesWithSubpages' => array( 0 => isset( $opts['subpage'] ) ),
780  'wgMaxTocLevel' => $maxtoclevel,
781  'wgCapitalLinks' => true,
782  'wgNoFollowLinks' => true,
783  'wgNoFollowDomainExceptions' => array(),
784  'wgThumbnailScriptPath' => false,
785  'wgUseImageResize' => true,
786  'wgSVGConverter' => 'null',
787  'wgSVGConverters' => array( 'null' => 'echo "1">$output' ),
788  'wgLocaltimezone' => 'UTC',
789  'wgAllowExternalImages' => self::getOptionValue( 'wgAllowExternalImages', $opts, true ),
790  'wgThumbLimits' => array( self::getOptionValue( 'thumbsize', $opts, 180 ) ),
791  'wgUseTidy' => false,
792  'wgDefaultLanguageVariant' => $variant,
793  'wgVariantArticlePath' => false,
794  'wgGroupPermissions' => array( '*' => array(
795  'createaccount' => true,
796  'read' => true,
797  'edit' => true,
798  'createpage' => true,
799  'createtalk' => true,
800  ) ),
801  'wgNamespaceProtection' => array( NS_MEDIAWIKI => 'editinterface' ),
802  'wgDefaultExternalStore' => array(),
803  'wgForeignFileRepos' => array(),
804  'wgLinkHolderBatchSize' => $linkHolderBatchSize,
805  'wgExperimentalHtmlIds' => false,
806  'wgExternalLinkTarget' => false,
807  'wgAlwaysUseTidy' => false,
808  'wgHtml5' => true,
809  'wgWellFormedXml' => true,
810  'wgAllowMicrodataAttributes' => true,
811  'wgAdaptiveMessageCache' => true,
812  'wgDisableLangConversion' => false,
813  'wgDisableTitleConversion' => false,
814  );
815 
816  if ( $config ) {
817  $configLines = explode( "\n", $config );
818 
819  foreach ( $configLines as $line ) {
820  list( $var, $value ) = explode( '=', $line, 2 );
821 
822  $settings[$var] = eval( "return $value;" );
823  }
824  }
825 
826  $this->savedGlobals = array();
827 
829  wfRunHooks( 'ParserTestGlobals', array( &$settings ) );
830 
831  foreach ( $settings as $var => $val ) {
832  if ( array_key_exists( $var, $GLOBALS ) ) {
833  $this->savedGlobals[$var] = $GLOBALS[$var];
834  }
835 
836  $GLOBALS[$var] = $val;
837  }
838 
839  $GLOBALS['wgContLang'] = Language::factory( $lang );
840  $GLOBALS['wgMemc'] = new EmptyBagOStuff;
841 
842  $context = new RequestContext();
843  $GLOBALS['wgLang'] = $context->getLanguage();
844  $GLOBALS['wgOut'] = $context->getOutput();
845  $GLOBALS['wgUser'] = $context->getUser();
846 
847  // We (re)set $wgThumbLimits to a single-element array above.
848  $context->getUser()->setOption( 'thumbsize', 0 );
849 
851 
852  $wgHooks['ParserTestParser'][] = 'ParserTestParserHook::setup';
853  $wgHooks['ParserGetVariableValueTs'][] = 'ParserTest::getFakeTimestamp';
854 
856 
857  return $context;
858  }
859 
864  private function listTables() {
865  $tables = array( 'user', 'user_properties', 'user_former_groups', 'page', 'page_restrictions',
866  'protected_titles', 'revision', 'text', 'pagelinks', 'imagelinks',
867  'categorylinks', 'templatelinks', 'externallinks', 'langlinks', 'iwlinks',
868  'site_stats', 'hitcounter', 'ipblocks', 'image', 'oldimage',
869  'recentchanges', 'watchlist', 'interwiki', 'logging',
870  'querycache', 'objectcache', 'job', 'l10n_cache', 'redirect', 'querycachetwo',
871  'archive', 'user_groups', 'page_props', 'category', 'msg_resource', 'msg_resource_links'
872  );
873 
874  if ( in_array( $this->db->getType(), array( 'mysql', 'sqlite', 'oracle' ) ) ) {
875  array_push( $tables, 'searchindex' );
876  }
877 
878  // Allow extensions to add to the list of tables to duplicate;
879  // may be necessary if they hook into page save or other code
880  // which will require them while running tests.
881  wfRunHooks( 'ParserTestTables', array( &$tables ) );
882 
883  return $tables;
884  }
885 
891  public function setupDatabase() {
892  global $wgDBprefix;
893 
894  if ( $this->databaseSetupDone ) {
895  return;
896  }
897 
898  $this->db = wfGetDB( DB_MASTER );
899  $dbType = $this->db->getType();
900 
901  if ( $wgDBprefix === 'parsertest_' || ( $dbType == 'oracle' && $wgDBprefix === 'pt_' ) ) {
902  throw new MWException( 'setupDatabase should be called before setupGlobals' );
903  }
904 
905  $this->databaseSetupDone = true;
906  $this->oldTablePrefix = $wgDBprefix;
907 
908  # SqlBagOStuff broke when using temporary tables on r40209 (bug 15892).
909  # It seems to have been fixed since (r55079?), but regressed at some point before r85701.
910  # This works around it for now...
912 
913  # CREATE TEMPORARY TABLE breaks if there is more than one server
914  if ( wfGetLB()->getServerCount() != 1 ) {
915  $this->useTemporaryTables = false;
916  }
917 
918  $temporary = $this->useTemporaryTables || $dbType == 'postgres';
919  $prefix = $dbType != 'oracle' ? 'parsertest_' : 'pt_';
920 
921  $this->dbClone = new CloneDatabase( $this->db, $this->listTables(), $prefix );
922  $this->dbClone->useTemporaryTables( $temporary );
923  $this->dbClone->cloneTableStructure();
924 
925  if ( $dbType == 'oracle' ) {
926  $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
927  # Insert 0 user to prevent FK violations
928 
929  # Anonymous user
930  $this->db->insert( 'user', array(
931  'user_id' => 0,
932  'user_name' => 'Anonymous' ) );
933  }
934 
935  # Update certain things in site_stats
936  $this->db->insert( 'site_stats',
937  array( 'ss_row_id' => 1, 'ss_images' => 2, 'ss_good_articles' => 1 ) );
938 
939  # Reinitialise the LocalisationCache to match the database state
940  Language::getLocalisationCache()->unloadAll();
941 
942  # Clear the message cache
943  MessageCache::singleton()->clear();
944 
945  // Remember to update newParserTests.php after changing the below
946  // (and it uses a slightly different syntax just for teh lulz)
947  $this->uploadDir = $this->setupUploadDir();
948  $user = User::createNew( 'WikiSysop' );
949  $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.jpg' ) );
950  # note that the size/width/height/bits/etc of the file
951  # are actually set by inspecting the file itself; the arguments
952  # to recordUpload2 have no effect. That said, we try to make things
953  # match up so it is less confusing to readers of the code & tests.
954  $image->recordUpload2( '', 'Upload of some lame file', 'Some lame file', array(
955  'size' => 7881,
956  'width' => 1941,
957  'height' => 220,
958  'bits' => 8,
959  'media_type' => MEDIATYPE_BITMAP,
960  'mime' => 'image/jpeg',
961  'metadata' => serialize( array() ),
962  'sha1' => wfBaseConvert( '1', 16, 36, 31 ),
963  'fileExists' => true
964  ), $this->db->timestamp( '20010115123500' ), $user );
965 
966  $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Thumb.png' ) );
967  # again, note that size/width/height below are ignored; see above.
968  $image->recordUpload2( '', 'Upload of some lame thumbnail', 'Some lame thumbnail', array(
969  'size' => 22589,
970  'width' => 135,
971  'height' => 135,
972  'bits' => 8,
973  'media_type' => MEDIATYPE_BITMAP,
974  'mime' => 'image/png',
975  'metadata' => serialize( array() ),
976  'sha1' => wfBaseConvert( '2', 16, 36, 31 ),
977  'fileExists' => true
978  ), $this->db->timestamp( '20130225203040' ), $user );
979 
980  $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.svg' ) );
981  $image->recordUpload2( '', 'Upload of some lame SVG', 'Some lame SVG', array(
982  'size' => 12345,
983  'width' => 240,
984  'height' => 180,
985  'bits' => 24,
986  'media_type' => MEDIATYPE_DRAWING,
987  'mime' => 'image/svg+xml',
988  'metadata' => serialize( array() ),
989  'sha1' => wfBaseConvert( '', 16, 36, 31 ),
990  'fileExists' => true
991  ), $this->db->timestamp( '20010115123500' ), $user );
992 
993  # This image will be blacklisted in [[MediaWiki:Bad image list]]
994  $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Bad.jpg' ) );
995  $image->recordUpload2( '', 'zomgnotcensored', 'Borderline image', array(
996  'size' => 12345,
997  'width' => 320,
998  'height' => 240,
999  'bits' => 24,
1000  'media_type' => MEDIATYPE_BITMAP,
1001  'mime' => 'image/jpeg',
1002  'metadata' => serialize( array() ),
1003  'sha1' => wfBaseConvert( '3', 16, 36, 31 ),
1004  'fileExists' => true
1005  ), $this->db->timestamp( '20010115123500' ), $user );
1006  }
1007 
1008  public function teardownDatabase() {
1009  if ( !$this->databaseSetupDone ) {
1010  $this->teardownGlobals();
1011  return;
1012  }
1013  $this->teardownUploadDir( $this->uploadDir );
1014 
1015  $this->dbClone->destroy();
1016  $this->databaseSetupDone = false;
1017 
1018  if ( $this->useTemporaryTables ) {
1019  if ( $this->db->getType() == 'sqlite' ) {
1020  # Under SQLite the searchindex table is virtual and need
1021  # to be explicitly destroyed. See bug 29912
1022  # See also MediaWikiTestCase::destroyDB()
1023  wfDebug( __METHOD__ . " explicitly destroying sqlite virtual table parsertest_searchindex\n" );
1024  $this->db->query( "DROP TABLE `parsertest_searchindex`" );
1025  }
1026  # Don't need to do anything
1027  $this->teardownGlobals();
1028  return;
1029  }
1030 
1031  $tables = $this->listTables();
1032 
1033  foreach ( $tables as $table ) {
1034  if ( $this->db->getType() == 'oracle' ) {
1035  $this->db->query( "DROP TABLE pt_$table DROP CONSTRAINTS" );
1036  } else {
1037  $this->db->query( "DROP TABLE `parsertest_$table`" );
1038  }
1039  }
1040 
1041  if ( $this->db->getType() == 'oracle' ) {
1042  $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
1043  }
1044 
1045  $this->teardownGlobals();
1046  }
1047 
1054  private function setupUploadDir() {
1055  global $IP;
1056 
1057  if ( $this->keepUploads ) {
1058  $dir = wfTempDir() . '/mwParser-images';
1059 
1060  if ( is_dir( $dir ) ) {
1061  return $dir;
1062  }
1063  } else {
1064  $dir = wfTempDir() . "/mwParser-" . mt_rand() . "-images";
1065  }
1066 
1067  // wfDebug( "Creating upload directory $dir\n" );
1068  if ( file_exists( $dir ) ) {
1069  wfDebug( "Already exists!\n" );
1070  return $dir;
1071  }
1072 
1073  wfMkdirParents( $dir . '/3/3a', null, __METHOD__ );
1074  copy( "$IP/skins/monobook/headbg.jpg", "$dir/3/3a/Foobar.jpg" );
1075  wfMkdirParents( $dir . '/e/ea', null, __METHOD__ );
1076  copy( "$IP/skins/monobook/wiki.png", "$dir/e/ea/Thumb.png" );
1077  wfMkdirParents( $dir . '/0/09', null, __METHOD__ );
1078  copy( "$IP/skins/monobook/headbg.jpg", "$dir/0/09/Bad.jpg" );
1079  wfMkdirParents( $dir . '/f/ff', null, __METHOD__ );
1080  file_put_contents( "$dir/f/ff/Foobar.svg",
1081  '<?xml version="1.0" encoding="utf-8"?>' .
1082  '<svg xmlns="http://www.w3.org/2000/svg"' .
1083  ' version="1.1" width="240" height="180"/>' );
1084  return $dir;
1085  }
1086 
1091  private function teardownGlobals() {
1095  LinkCache::singleton()->clear();
1096 
1097  foreach ( $this->savedGlobals as $var => $val ) {
1098  $GLOBALS[$var] = $val;
1099  }
1100  }
1101 
1105  private function teardownUploadDir( $dir ) {
1106  if ( $this->keepUploads ) {
1107  return;
1108  }
1109 
1110  // delete the files first, then the dirs.
1111  self::deleteFiles(
1112  array(
1113  "$dir/3/3a/Foobar.jpg",
1114  "$dir/thumb/3/3a/Foobar.jpg/1000px-Foobar.jpg",
1115  "$dir/thumb/3/3a/Foobar.jpg/100px-Foobar.jpg",
1116  "$dir/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg",
1117  "$dir/thumb/3/3a/Foobar.jpg/1280px-Foobar.jpg",
1118  "$dir/thumb/3/3a/Foobar.jpg/137px-Foobar.jpg",
1119  "$dir/thumb/3/3a/Foobar.jpg/1500px-Foobar.jpg",
1120  "$dir/thumb/3/3a/Foobar.jpg/177px-Foobar.jpg",
1121  "$dir/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg",
1122  "$dir/thumb/3/3a/Foobar.jpg/200px-Foobar.jpg",
1123  "$dir/thumb/3/3a/Foobar.jpg/206px-Foobar.jpg",
1124  "$dir/thumb/3/3a/Foobar.jpg/20px-Foobar.jpg",
1125  "$dir/thumb/3/3a/Foobar.jpg/220px-Foobar.jpg",
1126  "$dir/thumb/3/3a/Foobar.jpg/265px-Foobar.jpg",
1127  "$dir/thumb/3/3a/Foobar.jpg/270px-Foobar.jpg",
1128  "$dir/thumb/3/3a/Foobar.jpg/274px-Foobar.jpg",
1129  "$dir/thumb/3/3a/Foobar.jpg/300px-Foobar.jpg",
1130  "$dir/thumb/3/3a/Foobar.jpg/30px-Foobar.jpg",
1131  "$dir/thumb/3/3a/Foobar.jpg/330px-Foobar.jpg",
1132  "$dir/thumb/3/3a/Foobar.jpg/353px-Foobar.jpg",
1133  "$dir/thumb/3/3a/Foobar.jpg/360px-Foobar.jpg",
1134  "$dir/thumb/3/3a/Foobar.jpg/400px-Foobar.jpg",
1135  "$dir/thumb/3/3a/Foobar.jpg/40px-Foobar.jpg",
1136  "$dir/thumb/3/3a/Foobar.jpg/440px-Foobar.jpg",
1137  "$dir/thumb/3/3a/Foobar.jpg/442px-Foobar.jpg",
1138  "$dir/thumb/3/3a/Foobar.jpg/450px-Foobar.jpg",
1139  "$dir/thumb/3/3a/Foobar.jpg/50px-Foobar.jpg",
1140  "$dir/thumb/3/3a/Foobar.jpg/600px-Foobar.jpg",
1141  "$dir/thumb/3/3a/Foobar.jpg/640px-Foobar.jpg",
1142  "$dir/thumb/3/3a/Foobar.jpg/70px-Foobar.jpg",
1143  "$dir/thumb/3/3a/Foobar.jpg/75px-Foobar.jpg",
1144  "$dir/thumb/3/3a/Foobar.jpg/960px-Foobar.jpg",
1145 
1146  "$dir/e/ea/Thumb.png",
1147 
1148  "$dir/0/09/Bad.jpg",
1149 
1150  "$dir/f/ff/Foobar.svg",
1151  "$dir/thumb/f/ff/Foobar.svg/180px-Foobar.svg.png",
1152  "$dir/thumb/f/ff/Foobar.svg/2000px-Foobar.svg.png",
1153  "$dir/thumb/f/ff/Foobar.svg/270px-Foobar.svg.png",
1154  "$dir/thumb/f/ff/Foobar.svg/3000px-Foobar.svg.png",
1155  "$dir/thumb/f/ff/Foobar.svg/360px-Foobar.svg.png",
1156  "$dir/thumb/f/ff/Foobar.svg/4000px-Foobar.svg.png",
1157  "$dir/thumb/f/ff/Foobar.svg/langde-180px-Foobar.svg.png",
1158  "$dir/thumb/f/ff/Foobar.svg/langde-270px-Foobar.svg.png",
1159  "$dir/thumb/f/ff/Foobar.svg/langde-360px-Foobar.svg.png",
1160 
1161  "$dir/math/f/a/5/fa50b8b616463173474302ca3e63586b.png",
1162  )
1163  );
1164 
1165  self::deleteDirs(
1166  array(
1167  "$dir/3/3a",
1168  "$dir/3",
1169  "$dir/thumb/3/3a/Foobar.jpg",
1170  "$dir/thumb/3/3a",
1171  "$dir/thumb/3",
1172  "$dir/e/ea",
1173  "$dir/e",
1174  "$dir/f/ff/",
1175  "$dir/f/",
1176  "$dir/thumb/f/ff/Foobar.svg",
1177  "$dir/thumb/f/ff/",
1178  "$dir/thumb/f/",
1179  "$dir/0/09/",
1180  "$dir/0/",
1181  "$dir/thumb",
1182  "$dir/math/f/a/5",
1183  "$dir/math/f/a",
1184  "$dir/math/f",
1185  "$dir/math",
1186  "$dir",
1187  )
1188  );
1189  }
1190 
1195  private static function deleteFiles( $files ) {
1196  foreach ( $files as $file ) {
1197  if ( file_exists( $file ) ) {
1198  unlink( $file );
1199  }
1200  }
1201  }
1202 
1207  private static function deleteDirs( $dirs ) {
1208  foreach ( $dirs as $dir ) {
1209  if ( is_dir( $dir ) ) {
1210  rmdir( $dir );
1211  }
1212  }
1213  }
1214 
1218  protected function showTesting( $desc ) {
1219  print "Running test $desc... ";
1220  }
1221 
1230  protected function showSuccess( ParserTestResult $testResult ) {
1231  if ( $this->showProgress ) {
1232  print $this->term->color( '1;32' ) . 'PASSED' . $this->term->reset() . "\n";
1233  }
1234 
1235  return true;
1236  }
1237 
1247  protected function showFailure( ParserTestResult $testResult ) {
1248  if ( $this->showFailure ) {
1249  if ( !$this->showProgress ) {
1250  # In quiet mode we didn't show the 'Testing' message before the
1251  # test, in case it succeeded. Show it now:
1252  $this->showTesting( $testResult->description );
1253  }
1254 
1255  print $this->term->color( '31' ) . 'FAILED!' . $this->term->reset() . "\n";
1256 
1257  if ( $this->showOutput ) {
1258  print "--- Expected ---\n{$testResult->expected}\n";
1259  print "--- Actual ---\n{$testResult->actual}\n";
1260  }
1261 
1262  if ( $this->showDiffs ) {
1263  print $this->quickDiff( $testResult->expected, $testResult->actual );
1264  if ( !$this->wellFormed( $testResult->actual ) ) {
1265  print "XML error: $this->mXmlError\n";
1266  }
1267  }
1268  }
1269 
1270  return false;
1271  }
1272 
1283  protected function quickDiff( $input, $output,
1284  $inFileTail = 'expected', $outFileTail = 'actual'
1285  ) {
1286  # Windows, or at least the fc utility, is retarded
1287  $slash = wfIsWindows() ? '\\' : '/';
1288  $prefix = wfTempDir() . "{$slash}mwParser-" . mt_rand();
1289 
1290  $infile = "$prefix-$inFileTail";
1291  $this->dumpToFile( $input, $infile );
1292 
1293  $outfile = "$prefix-$outFileTail";
1294  $this->dumpToFile( $output, $outfile );
1295 
1296  $shellInfile = wfEscapeShellArg( $infile );
1297  $shellOutfile = wfEscapeShellArg( $outfile );
1298 
1299  global $wgDiff3;
1300  // we assume that people with diff3 also have usual diff
1301  $shellCommand = ( wfIsWindows() && !$wgDiff3 ) ? 'fc' : 'diff -au';
1302 
1303  $diff = wfShellExec( "$shellCommand $shellInfile $shellOutfile" );
1304 
1305  unlink( $infile );
1306  unlink( $outfile );
1307 
1308  return $this->colorDiff( $diff );
1309  }
1310 
1317  private function dumpToFile( $data, $filename ) {
1318  $file = fopen( $filename, "wt" );
1319  fwrite( $file, $data . "\n" );
1320  fclose( $file );
1321  }
1322 
1330  protected function colorDiff( $text ) {
1331  return preg_replace(
1332  array( '/^(-.*)$/m', '/^(\+.*)$/m' ),
1333  array( $this->term->color( 34 ) . '$1' . $this->term->reset(),
1334  $this->term->color( 31 ) . '$1' . $this->term->reset() ),
1335  $text );
1336  }
1337 
1343  public function showRunFile( $path ) {
1344  print $this->term->color( 1 ) .
1345  "Reading tests from \"$path\"..." .
1346  $this->term->reset() .
1347  "\n";
1348  }
1349 
1357  public static function addArticle( $name, $text, $line = 'unknown', $ignoreDuplicate = '' ) {
1358  global $wgCapitalLinks;
1359 
1360  $oldCapitalLinks = $wgCapitalLinks;
1361  $wgCapitalLinks = true; // We only need this from SetupGlobals() See r70917#c8637
1362 
1363  $text = self::chomp( $text );
1364  $name = self::chomp( $name );
1365 
1367 
1368  if ( is_null( $title ) ) {
1369  throw new MWException( "invalid title '$name' at line $line\n" );
1370  }
1371 
1372  $page = WikiPage::factory( $title );
1373  $page->loadPageData( 'fromdbmaster' );
1374 
1375  if ( $page->exists() ) {
1376  if ( $ignoreDuplicate == 'ignoreduplicate' ) {
1377  return;
1378  } else {
1379  throw new MWException( "duplicate article '$name' at line $line\n" );
1380  }
1381  }
1382 
1383  $page->doEditContent( ContentHandler::makeContent( $text, $title ), '', EDIT_NEW );
1384 
1385  $wgCapitalLinks = $oldCapitalLinks;
1386  }
1387 
1396  public function requireHook( $name ) {
1397  global $wgParser;
1398 
1399  $wgParser->firstCallInit(); // make sure hooks are loaded.
1400 
1401  if ( isset( $wgParser->mTagHooks[$name] ) ) {
1402  $this->hooks[$name] = $wgParser->mTagHooks[$name];
1403  } else {
1404  echo " This test suite requires the '$name' hook extension, skipping.\n";
1405  return false;
1406  }
1407 
1408  return true;
1409  }
1410 
1419  public function requireFunctionHook( $name ) {
1420  global $wgParser;
1421 
1422  $wgParser->firstCallInit(); // make sure hooks are loaded.
1423 
1424  if ( isset( $wgParser->mFunctionHooks[$name] ) ) {
1425  $this->functionHooks[$name] = $wgParser->mFunctionHooks[$name];
1426  } else {
1427  echo " This test suite requires the '$name' function hook extension, skipping.\n";
1428  return false;
1429  }
1430 
1431  return true;
1432  }
1433 
1441  private function tidy( $text ) {
1442  global $wgUseTidy;
1443 
1444  if ( $wgUseTidy ) {
1445  $text = MWTidy::tidy( $text );
1446  }
1447 
1448  return $text;
1449  }
1450 
1451  private function wellFormed( $text ) {
1452  $html =
1454  '<html>' .
1455  $text .
1456  '</html>';
1457 
1458  $parser = xml_parser_create( "UTF-8" );
1459 
1460  # case folding violates XML standard, turn it off
1461  xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
1462 
1463  if ( !xml_parse( $parser, $html, true ) ) {
1464  $err = xml_error_string( xml_get_error_code( $parser ) );
1465  $position = xml_get_current_byte_index( $parser );
1466  $fragment = $this->extractFragment( $html, $position );
1467  $this->mXmlError = "$err at byte $position:\n$fragment";
1468  xml_parser_free( $parser );
1469 
1470  return false;
1471  }
1472 
1473  xml_parser_free( $parser );
1474 
1475  return true;
1476  }
1477 
1478  private function extractFragment( $text, $position ) {
1479  $start = max( 0, $position - 10 );
1480  $before = $position - $start;
1481  $fragment = '...' .
1482  $this->term->color( 34 ) .
1483  substr( $text, $start, $before ) .
1484  $this->term->color( 0 ) .
1485  $this->term->color( 31 ) .
1486  $this->term->color( 1 ) .
1487  substr( $text, $position, 1 ) .
1488  $this->term->color( 0 ) .
1489  $this->term->color( 34 ) .
1490  substr( $text, $position + 1, 9 ) .
1491  $this->term->color( 0 ) .
1492  '...';
1493  $display = str_replace( "\n", ' ', $fragment );
1494  $caret = ' ' .
1495  str_repeat( ' ', $before ) .
1496  $this->term->color( 31 ) .
1497  '^' .
1498  $this->term->color( 0 );
1499 
1500  return "$display\n$caret";
1501  }
1502 
1503  static function getFakeTimestamp( &$parser, &$ts ) {
1504  $ts = 123; //parsed as '1970-01-01T00:02:03Z'
1505  return true;
1506  }
1507 }
DummyTermColorer
A colour-less terminal.
Definition: MWTerm.php:64
Title\makeTitle
static & makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:398
$wgUser
$wgUser
Definition: Setup.php:572
$result
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. $reader:XMLReader object $logInfo:Array of information Return false to stop further processing of the tag 'ImportHandlePageXMLTag':When parsing a XML tag in a page. $reader:XMLReader object $pageInfo:Array of information Return false to stop further processing of the tag 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information Return false to stop further processing of the tag 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. $reader:XMLReader object Return false to stop further processing of the tag 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. $reader:XMLReader object $revisionInfo:Array of information Return false to stop further processing of the tag 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. $title:Title object for the current page $request:WebRequest $ignoreRedirect:boolean to skip redirect check $target:Title/string of redirect target $article:Article object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) $article:article(object) being checked 'IsTrustedProxy':Override the result of wfIsTrustedProxy() $ip:IP being check $result:Change this value to override the result of wfIsTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of User::isValidEmailAddr(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetMagic':DEPRECATED, use $magicWords in a file listed in $wgExtensionMessagesFiles instead. Use this to define synonyms of magic words depending of the language $magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetSpecialPageAliases':DEPRECATED, use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language $specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Associative array mapping language codes to prefixed links of the form "language:title". & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LinkBegin':Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1528
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:189
DB_MASTER
const DB_MASTER
Definition: Defines.php:56
wfShellExec
wfShellExec( $cmd, &$retval=null, $environ=array(), $limits=array(), $options=array())
Execute a shell command, with time and memory limits mirrored from the PHP configuration if supported...
Definition: GlobalFunctions.php:2851
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
$files
$files
Definition: importImages.php:67
LockManagerGroup\destroySingletons
static destroySingletons()
Destroy the singleton instances.
Definition: LockManagerGroup.php:63
HashBagOStuff
This is a test of the interface, mainly.
Definition: HashBagOStuff.php:30
$html
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses & $html
Definition: hooks.txt:1530
$tables
namespace and then decline to actually register it RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist & $tables
Definition: hooks.txt:815
EmptyBagOStuff
A BagOStuff object with no objects in it.
Definition: EmptyBagOStuff.php:29
wfMkdirParents
wfMkdirParents( $dir, $mode=null, $caller=null)
Make directory, and make all parent directories if they don't exist.
Definition: GlobalFunctions.php:2637
ParserTestResult
Represent the result of a parser test.
Definition: ParserTestResult.php:15
ParserTestResult\isSuccess
isSuccess()
Whether the test passed.
Definition: ParserTestResult.php:42
StubObject
Class to implement stub globals, which are globals that delay loading the their associated module cod...
Definition: StubObject.php:44
$wgMemc
globals will be eliminated from MediaWiki replaced by an application object which would be passed to constructors Whether that would be an convenient solution remains to be but certainly PHP makes such object oriented programming models easier than they were in previous versions For the time being MediaWiki programmers will have to work in an environment with some global context At the time of globals were initialised on startup by MediaWiki of these were configuration which are documented in DefaultSettings php There is no comprehensive documentation for the remaining however some of the most important ones are listed below They are typically initialised either in index php or in Setup php For a description of the see design txt $wgTitle Title object created from the request URL $wgOut OutputPage object for HTTP response $wgUser User object for the user associated with the current request $wgLang Language object selected by user preferences $wgContLang Language object associated with the wiki being viewed $wgParser Parser object Parser extensions register their hooks here $wgRequest WebRequest to get request data $wgMemc
Definition: globals.txt:25
wfGetLB
wfGetLB( $wiki=false)
Get a load balancer object.
Definition: GlobalFunctions.php:3724
wfGetDB
& wfGetDB( $db, $groups=array(), $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3714
CACHE_NONE
const CACHE_NONE
Definition: Defines.php:112
character
</p > ! end ! test Bare pipe character(bug 52363) !! wikitext|!! html< p >|</p > !! end !! test Bare pipe character from a template(bug 52363) !! wikitext
Definition: parserTests.txt:918
$messageMemc
globals will be eliminated from MediaWiki replaced by an application object which would be passed to constructors Whether that would be an convenient solution remains to be but certainly PHP makes such object oriented programming models easier than they were in previous versions For the time being MediaWiki programmers will have to work in an environment with some global context At the time of globals were initialised on startup by MediaWiki of these were configuration which are documented in DefaultSettings php There is no comprehensive documentation for the remaining however some of the most important ones are listed below They are typically initialised either in index php or in Setup php For a description of the see design txt $wgTitle Title object created from the request URL $wgOut OutputPage object for HTTP response $wgUser User object for the user associated with the current request $wgLang Language object selected by user preferences $wgContLang Language object associated with the wiki being viewed $wgParser Parser object Parser extensions register their hooks here $wgRequest WebRequest to get request data $messageMemc
Definition: globals.txt:25
NS_FILE
const NS_FILE
Definition: Defines.php:85
$parserMemc
controlled by $wgMainCacheType * $parserMemc
Definition: memcached.txt:78
$s
$s
Definition: mergeMessageFileList.php:156
Hooks\clear
static clear( $name)
Clears hooks registered via Hooks::register().
Definition: Hooks.php:72
DeferredUpdates\clearPendingUpdates
static clearPendingUpdates()
Clear all pending updates without performing them.
Definition: DeferredUpdates.php:128
$wgHooks
$wgHooks['ArticleShow'][]
Definition: hooks.txt:110
Preprocessor_Hash
Differences from DOM schema:
Definition: Preprocessor_Hash.php:30
Linker\formatComment
static formatComment( $comment, $title=null, $local=false)
This function is called by all recent changes variants, by the page history, and by the user contribu...
Definition: Linker.php:1263
$flags
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2124
ContextSource\getLanguage
getLanguage()
Get the Language object.
Definition: ContextSource.php:154
FileBackendGroup\destroySingleton
static destroySingleton()
Destroy the singleton instance.
Definition: FileBackendGroup.php:55
NS_MAIN
const NS_MAIN
Definition: Defines.php:79
Sanitizer\hackDocType
static hackDocType()
Hack up a private DOCTYPE with HTML's standard entity declarations.
Definition: Sanitizer.php:1755
FormatJson\decode
static decode( $value, $assoc=false)
Decodes a JSON string.
Definition: FormatJson.php:126
Language\getLocalisationCache
static getLocalisationCache()
Get the LocalisationCache instance.
Definition: Language.php:443
wfGetMainCache
wfGetMainCache()
Get the main cache object.
Definition: GlobalFunctions.php:4022
MWException
MediaWiki exception.
Definition: MWException.php:26
$out
$out
Definition: UtfNormalGenerate.php:167
WikiPage\factory
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
Definition: WikiPage.php:103
hooks
Using a hook running we can avoid having all this option specific stuff in our mainline code Using hooks
Definition: hooks.txt:73
$parser
do that in ParserLimitReportFormat instead $parser
Definition: hooks.txt:1961
ObjectCache\$instances
static $instances
Definition: ObjectCache.php:30
TestRecorder
Definition: testHelpers.inc:53
$wgOut
$wgOut
Definition: Setup.php:582
wfRunHooks
wfRunHooks( $event, array $args=array(), $deprecatedVersion=null)
Call hook functions defined in $wgHooks.
Definition: GlobalFunctions.php:4066
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
$dirs
$dirs
Definition: mergeMessageFileList.php:163
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
RequestContext
Group all the pieces relevant to the context of a request into one instance.
Definition: RequestContext.php:30
AnsiTermColorer
Terminal that supports ANSI escape sequences.
Definition: MWTerm.php:31
User\createNew
static createNew( $name, $params=array())
Add a user to the database, return the user object.
Definition: User.php:3461
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
MessageCache\singleton
static singleton()
Get the signleton instance of this class.
Definition: MessageCache.php:101
DBError
Database error base class.
Definition: DatabaseError.php:28
$wgLockManagers
$wgLockManagers[]
Initialise $wgLockManagers to include basic FS version.
Definition: Setup.php:148
ContentHandler\makeContent
static makeContent( $text, Title $title=null, $modelId=null, $format=null)
Convenience function for creating a Content object from a given textual representation.
Definition: ContentHandler.php:144
$options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1530
MagicWord\clearCache
static clearCache()
Clear the self::$mObjects variable For use in parser tests.
Definition: MagicWord.php:300
$ok
$ok
Definition: UtfNormalTest.php:71
$section
$section
Definition: Utf8Test.php:88
$line
$line
Definition: cdb.php:57
wfDebug
wfDebug( $text, $dest='all')
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:980
RepoGroup\destroySingleton
static destroySingleton()
Destroy the singleton instance, so that a new one will be created next time singleton() is called.
Definition: RepoGroup.php:67
DbTestPreviewer
Definition: testHelpers.inc:101
$title
presenting them properly to the user as errors is done by the caller $title
Definition: hooks.txt:1324
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:336
Hooks\register
static register( $name, $callback)
Attach an event handler to a given hook.
Definition: Hooks.php:55
$matches
if(!defined( 'MEDIAWIKI')) if(!isset( $wgVersion)) $matches
Definition: NoLocalSettings.php:33
$value
$value
Definition: styleTest.css.php:45
TestFileIterator
Definition: testHelpers.inc:352
ParserOptions\newFromContext
static newFromContext(IContextSource $context)
Get a ParserOptions object from a IContextSource object.
Definition: ParserOptions.php:396
$wgNamespaceAliases
$wgNamespaceAliases['Image']
The canonical names of namespaces 6 and 7 are, as of v1.14, "File" and "File_talk".
Definition: Setup.php:142
options
We ve cleaned up the code here by removing clumps of infrequently used code and moving them off somewhere else It s much easier for someone working with this code to see what s _really_ going and make changes or fix bugs In we can take all the code that deals with the little used title reversing options(say) and put it in one place. Instead of having little title-reversing if-blocks spread all over the codebase in showAnArticle
DatabaseBase
Database abstraction object.
Definition: Database.php:219
wfIsWindows
wfIsWindows()
Check if the operating system is Windows.
Definition: GlobalFunctions.php:2571
wfEscapeShellArg
wfEscapeShellArg()
Windows-compatible version of escapeshellarg() Windows doesn't recognise single-quotes in the shell,...
Definition: GlobalFunctions.php:2752
Maintenance\posix_isatty
static posix_isatty( $fd)
Wrapper for posix_isatty() We default as considering stdin a tty (for nice readline methods) but trea...
Definition: Maintenance.php:1121
wfGetParserCacheStorage
wfGetParserCacheStorage()
Get the cache object used by the parser cache.
Definition: GlobalFunctions.php:4042
showDiffs
showDiffs( $a, $b)
Definition: RandomTest.php:60
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:237
$file
if(PHP_SAPI !='cli') $file
Definition: UtfNormalTest2.php:30
EDIT_NEW
const EDIT_NEW
Definition: Defines.php:189
$wgArticlePath
$wgArticlePath
Definition: img_auth.php:48
wfGetMessageCacheStorage
wfGetMessageCacheStorage()
Get the cache object used by the message cache.
Definition: GlobalFunctions.php:4032
DbTestRecorder
Definition: testHelpers.inc:297
$wgLang
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as $wgLang
Definition: design.txt:56
wfTempDir
wfTempDir()
Tries to get the system directory for temporary files.
Definition: GlobalFunctions.php:2611
CloneDatabase
Definition: CloneDatabase.php:27
FSFileBackend
Class for a file system (FS) based file backend.
Definition: FSFileBackend.php:41
$wgParser
$wgParser
Definition: Setup.php:587
$dir
if(count( $args)==0) $dir
Definition: importImages.php:49
in
Prior to maintenance scripts were a hodgepodge of code that had no cohesion or formal method of action Beginning in
Definition: maintenance.txt:1
wfBaseConvert
wfBaseConvert( $input, $sourceBase, $destBase, $pad=1, $lowercase=true, $engine='auto')
Convert an arbitrarily-long digit string from one numeric base to another, optionally zero-padding to...
Definition: GlobalFunctions.php:3432
color
in the sidebar</td >< td > font color
Definition: All_system_messages.txt:425
$output
& $output
Definition: hooks.txt:375
$path
$path
Definition: NoLocalSettings.php:35
$wgMainCacheType
CACHE_MEMCACHED $wgMainCacheType
Definition: memcached.txt:63
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
User
User
Definition: All_system_messages.txt:425
$wgNamespaceProtection
if(!isset( $wgVersion)) if( $wgScript===false) if( $wgLoadScript===false) if( $wgArticlePath===false) if(!empty( $wgActionPaths) &&!isset( $wgActionPaths['view'])) if( $wgStylePath===false) if( $wgLocalStylePath===false) if( $wgStyleDirectory===false) if( $wgExtensionAssetsPath===false) if( $wgLogo===false) if( $wgUploadPath===false) if( $wgUploadDirectory===false) if( $wgReadOnlyFile===false) if( $wgFileCacheDirectory===false) if( $wgDeletedDirectory===false) if(isset( $wgFileStore['deleted']['directory'])) if(isset( $wgFooterIcons['copyright']) &&isset( $wgFooterIcons['copyright']['copyright']) && $wgFooterIcons['copyright']['copyright']===array()) if(isset( $wgFooterIcons['poweredby']) &&isset( $wgFooterIcons['poweredby']['mediawiki']) && $wgFooterIcons['poweredby']['mediawiki']['src']===null) $wgNamespaceProtection[NS_MEDIAWIKI]
Unconditional protection for NS_MEDIAWIKI since otherwise it's too easy for a sysadmin to set $wgName...
Definition: Setup.php:135
Language\factory
static factory( $code)
Get a cached or new language object for a given language code.
Definition: Language.php:184
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:87
$t
$t
Definition: testCompression.php:65
term
which are not or by specifying more than one search term(only pages containing all of the search terms will appear in the result).</td >< td >
Definition: All_system_messages.txt:3012
NS_FILE_TALK
const NS_FILE_TALK
Definition: Defines.php:86
$IP
$IP
Definition: WebStart.php:92
wfLocalFile
wfLocalFile( $title)
Get an object referring to a locally registered file.
Definition: GlobalFunctions.php:3768
LinkCache\singleton
static & singleton()
Get an instance of this class.
Definition: LinkCache.php:49
href
shown</td >< td > a href
Definition: All_system_messages.txt:2674
MEDIATYPE_DRAWING
const MEDIATYPE_DRAWING
Definition: Defines.php:127
$GLOBALS
$GLOBALS['IP']
Definition: ComposerHookHandler.php:6
CACHE_DB
const CACHE_DB
Definition: Defines.php:113
ParserOptions\newFromUser
static newFromUser( $user)
Get a ParserOptions object from a given user.
Definition: ParserOptions.php:375
$e
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:1632
SpecialVersion\getVersion
static getVersion( $flags='')
Return a string of the MediaWiki version with SVN revision if available.
Definition: SpecialVersion.php:246
MWTidy\tidy
static tidy( $text)
Interface with html tidy, used if $wgUseTidy = true.
Definition: Tidy.php:126
MEDIATYPE_BITMAP
const MEDIATYPE_BITMAP
Definition: Defines.php:125