MediaWiki  1.23.15
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  'wgAllowMicrodataAttributes' => true,
810  'wgAdaptiveMessageCache' => true,
811  'wgDisableLangConversion' => false,
812  'wgDisableTitleConversion' => false,
813  );
814 
815  if ( $config ) {
816  $configLines = explode( "\n", $config );
817 
818  foreach ( $configLines as $line ) {
819  list( $var, $value ) = explode( '=', $line, 2 );
820 
821  $settings[$var] = eval( "return $value;" );
822  }
823  }
824 
825  $this->savedGlobals = array();
826 
828  wfRunHooks( 'ParserTestGlobals', array( &$settings ) );
829 
830  foreach ( $settings as $var => $val ) {
831  if ( array_key_exists( $var, $GLOBALS ) ) {
832  $this->savedGlobals[$var] = $GLOBALS[$var];
833  }
834 
835  $GLOBALS[$var] = $val;
836  }
837 
838  $GLOBALS['wgContLang'] = Language::factory( $lang );
839  $GLOBALS['wgMemc'] = new EmptyBagOStuff;
840 
841  $context = new RequestContext();
842  $GLOBALS['wgLang'] = $context->getLanguage();
843  $GLOBALS['wgOut'] = $context->getOutput();
844  $GLOBALS['wgUser'] = $context->getUser();
845 
846  // We (re)set $wgThumbLimits to a single-element array above.
847  $context->getUser()->setOption( 'thumbsize', 0 );
848 
850 
851  $wgHooks['ParserTestParser'][] = 'ParserTestParserHook::setup';
852  $wgHooks['ParserGetVariableValueTs'][] = 'ParserTest::getFakeTimestamp';
853 
855 
856  return $context;
857  }
858 
863  private function listTables() {
864  $tables = array( 'user', 'user_properties', 'user_former_groups', 'page', 'page_restrictions',
865  'protected_titles', 'revision', 'text', 'pagelinks', 'imagelinks',
866  'categorylinks', 'templatelinks', 'externallinks', 'langlinks', 'iwlinks',
867  'site_stats', 'hitcounter', 'ipblocks', 'image', 'oldimage',
868  'recentchanges', 'watchlist', 'interwiki', 'logging',
869  'querycache', 'objectcache', 'job', 'l10n_cache', 'redirect', 'querycachetwo',
870  'archive', 'user_groups', 'page_props', 'category', 'msg_resource', 'msg_resource_links'
871  );
872 
873  if ( in_array( $this->db->getType(), array( 'mysql', 'sqlite', 'oracle' ) ) ) {
874  array_push( $tables, 'searchindex' );
875  }
876 
877  // Allow extensions to add to the list of tables to duplicate;
878  // may be necessary if they hook into page save or other code
879  // which will require them while running tests.
880  wfRunHooks( 'ParserTestTables', array( &$tables ) );
881 
882  return $tables;
883  }
884 
890  public function setupDatabase() {
891  global $wgDBprefix;
892 
893  if ( $this->databaseSetupDone ) {
894  return;
895  }
896 
897  $this->db = wfGetDB( DB_MASTER );
898  $dbType = $this->db->getType();
899 
900  if ( $wgDBprefix === 'parsertest_' || ( $dbType == 'oracle' && $wgDBprefix === 'pt_' ) ) {
901  throw new MWException( 'setupDatabase should be called before setupGlobals' );
902  }
903 
904  $this->databaseSetupDone = true;
905  $this->oldTablePrefix = $wgDBprefix;
906 
907  # SqlBagOStuff broke when using temporary tables on r40209 (bug 15892).
908  # It seems to have been fixed since (r55079?), but regressed at some point before r85701.
909  # This works around it for now...
911 
912  # CREATE TEMPORARY TABLE breaks if there is more than one server
913  if ( wfGetLB()->getServerCount() != 1 ) {
914  $this->useTemporaryTables = false;
915  }
916 
917  $temporary = $this->useTemporaryTables || $dbType == 'postgres';
918  $prefix = $dbType != 'oracle' ? 'parsertest_' : 'pt_';
919 
920  $this->dbClone = new CloneDatabase( $this->db, $this->listTables(), $prefix );
921  $this->dbClone->useTemporaryTables( $temporary );
922  $this->dbClone->cloneTableStructure();
923 
924  if ( $dbType == 'oracle' ) {
925  $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
926  # Insert 0 user to prevent FK violations
927 
928  # Anonymous user
929  $this->db->insert( 'user', array(
930  'user_id' => 0,
931  'user_name' => 'Anonymous' ) );
932  }
933 
934  # Update certain things in site_stats
935  $this->db->insert( 'site_stats',
936  array( 'ss_row_id' => 1, 'ss_images' => 2, 'ss_good_articles' => 1 ) );
937 
938  # Reinitialise the LocalisationCache to match the database state
939  Language::getLocalisationCache()->unloadAll();
940 
941  # Clear the message cache
942  MessageCache::singleton()->clear();
943 
944  // Remember to update newParserTests.php after changing the below
945  // (and it uses a slightly different syntax just for teh lulz)
946  $this->uploadDir = $this->setupUploadDir();
947  $user = User::createNew( 'WikiSysop' );
948  $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.jpg' ) );
949  # note that the size/width/height/bits/etc of the file
950  # are actually set by inspecting the file itself; the arguments
951  # to recordUpload2 have no effect. That said, we try to make things
952  # match up so it is less confusing to readers of the code & tests.
953  $image->recordUpload2( '', 'Upload of some lame file', 'Some lame file', array(
954  'size' => 7881,
955  'width' => 1941,
956  'height' => 220,
957  'bits' => 8,
958  'media_type' => MEDIATYPE_BITMAP,
959  'mime' => 'image/jpeg',
960  'metadata' => serialize( array() ),
961  'sha1' => wfBaseConvert( '1', 16, 36, 31 ),
962  'fileExists' => true
963  ), $this->db->timestamp( '20010115123500' ), $user );
964 
965  $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Thumb.png' ) );
966  # again, note that size/width/height below are ignored; see above.
967  $image->recordUpload2( '', 'Upload of some lame thumbnail', 'Some lame thumbnail', array(
968  'size' => 22589,
969  'width' => 135,
970  'height' => 135,
971  'bits' => 8,
972  'media_type' => MEDIATYPE_BITMAP,
973  'mime' => 'image/png',
974  'metadata' => serialize( array() ),
975  'sha1' => wfBaseConvert( '2', 16, 36, 31 ),
976  'fileExists' => true
977  ), $this->db->timestamp( '20130225203040' ), $user );
978 
979  $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.svg' ) );
980  $image->recordUpload2( '', 'Upload of some lame SVG', 'Some lame SVG', array(
981  'size' => 12345,
982  'width' => 240,
983  'height' => 180,
984  'bits' => 24,
985  'media_type' => MEDIATYPE_DRAWING,
986  'mime' => 'image/svg+xml',
987  'metadata' => serialize( array() ),
988  'sha1' => wfBaseConvert( '', 16, 36, 31 ),
989  'fileExists' => true
990  ), $this->db->timestamp( '20010115123500' ), $user );
991 
992  # This image will be blacklisted in [[MediaWiki:Bad image list]]
993  $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Bad.jpg' ) );
994  $image->recordUpload2( '', 'zomgnotcensored', 'Borderline image', array(
995  'size' => 12345,
996  'width' => 320,
997  'height' => 240,
998  'bits' => 24,
999  'media_type' => MEDIATYPE_BITMAP,
1000  'mime' => 'image/jpeg',
1001  'metadata' => serialize( array() ),
1002  'sha1' => wfBaseConvert( '3', 16, 36, 31 ),
1003  'fileExists' => true
1004  ), $this->db->timestamp( '20010115123500' ), $user );
1005  }
1006 
1007  public function teardownDatabase() {
1008  if ( !$this->databaseSetupDone ) {
1009  $this->teardownGlobals();
1010  return;
1011  }
1012  $this->teardownUploadDir( $this->uploadDir );
1013 
1014  $this->dbClone->destroy();
1015  $this->databaseSetupDone = false;
1016 
1017  if ( $this->useTemporaryTables ) {
1018  if ( $this->db->getType() == 'sqlite' ) {
1019  # Under SQLite the searchindex table is virtual and need
1020  # to be explicitly destroyed. See bug 29912
1021  # See also MediaWikiTestCase::destroyDB()
1022  wfDebug( __METHOD__ . " explicitly destroying sqlite virtual table parsertest_searchindex\n" );
1023  $this->db->query( "DROP TABLE `parsertest_searchindex`" );
1024  }
1025  # Don't need to do anything
1026  $this->teardownGlobals();
1027  return;
1028  }
1029 
1030  $tables = $this->listTables();
1031 
1032  foreach ( $tables as $table ) {
1033  if ( $this->db->getType() == 'oracle' ) {
1034  $this->db->query( "DROP TABLE pt_$table DROP CONSTRAINTS" );
1035  } else {
1036  $this->db->query( "DROP TABLE `parsertest_$table`" );
1037  }
1038  }
1039 
1040  if ( $this->db->getType() == 'oracle' ) {
1041  $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
1042  }
1043 
1044  $this->teardownGlobals();
1045  }
1046 
1053  private function setupUploadDir() {
1054  global $IP;
1055 
1056  if ( $this->keepUploads ) {
1057  $dir = wfTempDir() . '/mwParser-images';
1058 
1059  if ( is_dir( $dir ) ) {
1060  return $dir;
1061  }
1062  } else {
1063  $dir = wfTempDir() . "/mwParser-" . mt_rand() . "-images";
1064  }
1065 
1066  // wfDebug( "Creating upload directory $dir\n" );
1067  if ( file_exists( $dir ) ) {
1068  wfDebug( "Already exists!\n" );
1069  return $dir;
1070  }
1071 
1072  wfMkdirParents( $dir . '/3/3a', null, __METHOD__ );
1073  copy( "$IP/skins/monobook/headbg.jpg", "$dir/3/3a/Foobar.jpg" );
1074  wfMkdirParents( $dir . '/e/ea', null, __METHOD__ );
1075  copy( "$IP/skins/monobook/wiki.png", "$dir/e/ea/Thumb.png" );
1076  wfMkdirParents( $dir . '/0/09', null, __METHOD__ );
1077  copy( "$IP/skins/monobook/headbg.jpg", "$dir/0/09/Bad.jpg" );
1078  wfMkdirParents( $dir . '/f/ff', null, __METHOD__ );
1079  file_put_contents( "$dir/f/ff/Foobar.svg",
1080  '<?xml version="1.0" encoding="utf-8"?>' .
1081  '<svg xmlns="http://www.w3.org/2000/svg"' .
1082  ' version="1.1" width="240" height="180"/>' );
1083  return $dir;
1084  }
1085 
1090  private function teardownGlobals() {
1094  LinkCache::singleton()->clear();
1095 
1096  foreach ( $this->savedGlobals as $var => $val ) {
1097  $GLOBALS[$var] = $val;
1098  }
1099  }
1100 
1104  private function teardownUploadDir( $dir ) {
1105  if ( $this->keepUploads ) {
1106  return;
1107  }
1108 
1109  // delete the files first, then the dirs.
1110  self::deleteFiles(
1111  array(
1112  "$dir/3/3a/Foobar.jpg",
1113  "$dir/thumb/3/3a/Foobar.jpg/1000px-Foobar.jpg",
1114  "$dir/thumb/3/3a/Foobar.jpg/100px-Foobar.jpg",
1115  "$dir/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg",
1116  "$dir/thumb/3/3a/Foobar.jpg/1280px-Foobar.jpg",
1117  "$dir/thumb/3/3a/Foobar.jpg/137px-Foobar.jpg",
1118  "$dir/thumb/3/3a/Foobar.jpg/1500px-Foobar.jpg",
1119  "$dir/thumb/3/3a/Foobar.jpg/177px-Foobar.jpg",
1120  "$dir/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg",
1121  "$dir/thumb/3/3a/Foobar.jpg/200px-Foobar.jpg",
1122  "$dir/thumb/3/3a/Foobar.jpg/206px-Foobar.jpg",
1123  "$dir/thumb/3/3a/Foobar.jpg/20px-Foobar.jpg",
1124  "$dir/thumb/3/3a/Foobar.jpg/220px-Foobar.jpg",
1125  "$dir/thumb/3/3a/Foobar.jpg/265px-Foobar.jpg",
1126  "$dir/thumb/3/3a/Foobar.jpg/270px-Foobar.jpg",
1127  "$dir/thumb/3/3a/Foobar.jpg/274px-Foobar.jpg",
1128  "$dir/thumb/3/3a/Foobar.jpg/300px-Foobar.jpg",
1129  "$dir/thumb/3/3a/Foobar.jpg/30px-Foobar.jpg",
1130  "$dir/thumb/3/3a/Foobar.jpg/330px-Foobar.jpg",
1131  "$dir/thumb/3/3a/Foobar.jpg/353px-Foobar.jpg",
1132  "$dir/thumb/3/3a/Foobar.jpg/360px-Foobar.jpg",
1133  "$dir/thumb/3/3a/Foobar.jpg/400px-Foobar.jpg",
1134  "$dir/thumb/3/3a/Foobar.jpg/40px-Foobar.jpg",
1135  "$dir/thumb/3/3a/Foobar.jpg/440px-Foobar.jpg",
1136  "$dir/thumb/3/3a/Foobar.jpg/442px-Foobar.jpg",
1137  "$dir/thumb/3/3a/Foobar.jpg/450px-Foobar.jpg",
1138  "$dir/thumb/3/3a/Foobar.jpg/50px-Foobar.jpg",
1139  "$dir/thumb/3/3a/Foobar.jpg/600px-Foobar.jpg",
1140  "$dir/thumb/3/3a/Foobar.jpg/640px-Foobar.jpg",
1141  "$dir/thumb/3/3a/Foobar.jpg/70px-Foobar.jpg",
1142  "$dir/thumb/3/3a/Foobar.jpg/75px-Foobar.jpg",
1143  "$dir/thumb/3/3a/Foobar.jpg/960px-Foobar.jpg",
1144 
1145  "$dir/e/ea/Thumb.png",
1146 
1147  "$dir/0/09/Bad.jpg",
1148 
1149  "$dir/f/ff/Foobar.svg",
1150  "$dir/thumb/f/ff/Foobar.svg/180px-Foobar.svg.png",
1151  "$dir/thumb/f/ff/Foobar.svg/2000px-Foobar.svg.png",
1152  "$dir/thumb/f/ff/Foobar.svg/270px-Foobar.svg.png",
1153  "$dir/thumb/f/ff/Foobar.svg/3000px-Foobar.svg.png",
1154  "$dir/thumb/f/ff/Foobar.svg/360px-Foobar.svg.png",
1155  "$dir/thumb/f/ff/Foobar.svg/4000px-Foobar.svg.png",
1156  "$dir/thumb/f/ff/Foobar.svg/langde-180px-Foobar.svg.png",
1157  "$dir/thumb/f/ff/Foobar.svg/langde-270px-Foobar.svg.png",
1158  "$dir/thumb/f/ff/Foobar.svg/langde-360px-Foobar.svg.png",
1159 
1160  "$dir/math/f/a/5/fa50b8b616463173474302ca3e63586b.png",
1161  )
1162  );
1163 
1164  self::deleteDirs(
1165  array(
1166  "$dir/3/3a",
1167  "$dir/3",
1168  "$dir/thumb/3/3a/Foobar.jpg",
1169  "$dir/thumb/3/3a",
1170  "$dir/thumb/3",
1171  "$dir/e/ea",
1172  "$dir/e",
1173  "$dir/f/ff/",
1174  "$dir/f/",
1175  "$dir/thumb/f/ff/Foobar.svg",
1176  "$dir/thumb/f/ff/",
1177  "$dir/thumb/f/",
1178  "$dir/0/09/",
1179  "$dir/0/",
1180  "$dir/thumb",
1181  "$dir/math/f/a/5",
1182  "$dir/math/f/a",
1183  "$dir/math/f",
1184  "$dir/math",
1185  "$dir",
1186  )
1187  );
1188  }
1189 
1194  private static function deleteFiles( $files ) {
1195  foreach ( $files as $file ) {
1196  if ( file_exists( $file ) ) {
1197  unlink( $file );
1198  }
1199  }
1200  }
1201 
1206  private static function deleteDirs( $dirs ) {
1207  foreach ( $dirs as $dir ) {
1208  if ( is_dir( $dir ) ) {
1209  rmdir( $dir );
1210  }
1211  }
1212  }
1213 
1217  protected function showTesting( $desc ) {
1218  print "Running test $desc... ";
1219  }
1220 
1229  protected function showSuccess( ParserTestResult $testResult ) {
1230  if ( $this->showProgress ) {
1231  print $this->term->color( '1;32' ) . 'PASSED' . $this->term->reset() . "\n";
1232  }
1233 
1234  return true;
1235  }
1236 
1246  protected function showFailure( ParserTestResult $testResult ) {
1247  if ( $this->showFailure ) {
1248  if ( !$this->showProgress ) {
1249  # In quiet mode we didn't show the 'Testing' message before the
1250  # test, in case it succeeded. Show it now:
1251  $this->showTesting( $testResult->description );
1252  }
1253 
1254  print $this->term->color( '31' ) . 'FAILED!' . $this->term->reset() . "\n";
1255 
1256  if ( $this->showOutput ) {
1257  print "--- Expected ---\n{$testResult->expected}\n";
1258  print "--- Actual ---\n{$testResult->actual}\n";
1259  }
1260 
1261  if ( $this->showDiffs ) {
1262  print $this->quickDiff( $testResult->expected, $testResult->actual );
1263  if ( !$this->wellFormed( $testResult->actual ) ) {
1264  print "XML error: $this->mXmlError\n";
1265  }
1266  }
1267  }
1268 
1269  return false;
1270  }
1271 
1282  protected function quickDiff( $input, $output,
1283  $inFileTail = 'expected', $outFileTail = 'actual'
1284  ) {
1285  # Windows, or at least the fc utility, is retarded
1286  $slash = wfIsWindows() ? '\\' : '/';
1287  $prefix = wfTempDir() . "{$slash}mwParser-" . mt_rand();
1288 
1289  $infile = "$prefix-$inFileTail";
1290  $this->dumpToFile( $input, $infile );
1291 
1292  $outfile = "$prefix-$outFileTail";
1293  $this->dumpToFile( $output, $outfile );
1294 
1295  $shellInfile = wfEscapeShellArg( $infile );
1296  $shellOutfile = wfEscapeShellArg( $outfile );
1297 
1298  global $wgDiff3;
1299  // we assume that people with diff3 also have usual diff
1300  $shellCommand = ( wfIsWindows() && !$wgDiff3 ) ? 'fc' : 'diff -au';
1301 
1302  $diff = wfShellExec( "$shellCommand $shellInfile $shellOutfile" );
1303 
1304  unlink( $infile );
1305  unlink( $outfile );
1306 
1307  return $this->colorDiff( $diff );
1308  }
1309 
1316  private function dumpToFile( $data, $filename ) {
1317  $file = fopen( $filename, "wt" );
1318  fwrite( $file, $data . "\n" );
1319  fclose( $file );
1320  }
1321 
1329  protected function colorDiff( $text ) {
1330  return preg_replace(
1331  array( '/^(-.*)$/m', '/^(\+.*)$/m' ),
1332  array( $this->term->color( 34 ) . '$1' . $this->term->reset(),
1333  $this->term->color( 31 ) . '$1' . $this->term->reset() ),
1334  $text );
1335  }
1336 
1342  public function showRunFile( $path ) {
1343  print $this->term->color( 1 ) .
1344  "Reading tests from \"$path\"..." .
1345  $this->term->reset() .
1346  "\n";
1347  }
1348 
1356  public static function addArticle( $name, $text, $line = 'unknown', $ignoreDuplicate = '' ) {
1357  global $wgCapitalLinks;
1358 
1359  $oldCapitalLinks = $wgCapitalLinks;
1360  $wgCapitalLinks = true; // We only need this from SetupGlobals() See r70917#c8637
1361 
1362  $text = self::chomp( $text );
1363  $name = self::chomp( $name );
1364 
1366 
1367  if ( is_null( $title ) ) {
1368  throw new MWException( "invalid title '$name' at line $line\n" );
1369  }
1370 
1371  $page = WikiPage::factory( $title );
1372  $page->loadPageData( 'fromdbmaster' );
1373 
1374  if ( $page->exists() ) {
1375  if ( $ignoreDuplicate == 'ignoreduplicate' ) {
1376  return;
1377  } else {
1378  throw new MWException( "duplicate article '$name' at line $line\n" );
1379  }
1380  }
1381 
1382  $page->doEditContent( ContentHandler::makeContent( $text, $title ), '', EDIT_NEW );
1383 
1384  $wgCapitalLinks = $oldCapitalLinks;
1385  }
1386 
1395  public function requireHook( $name ) {
1396  global $wgParser;
1397 
1398  $wgParser->firstCallInit(); // make sure hooks are loaded.
1399 
1400  if ( isset( $wgParser->mTagHooks[$name] ) ) {
1401  $this->hooks[$name] = $wgParser->mTagHooks[$name];
1402  } else {
1403  echo " This test suite requires the '$name' hook extension, skipping.\n";
1404  return false;
1405  }
1406 
1407  return true;
1408  }
1409 
1418  public function requireFunctionHook( $name ) {
1419  global $wgParser;
1420 
1421  $wgParser->firstCallInit(); // make sure hooks are loaded.
1422 
1423  if ( isset( $wgParser->mFunctionHooks[$name] ) ) {
1424  $this->functionHooks[$name] = $wgParser->mFunctionHooks[$name];
1425  } else {
1426  echo " This test suite requires the '$name' function hook extension, skipping.\n";
1427  return false;
1428  }
1429 
1430  return true;
1431  }
1432 
1440  private function tidy( $text ) {
1441  global $wgUseTidy;
1442 
1443  if ( $wgUseTidy ) {
1444  $text = MWTidy::tidy( $text );
1445  }
1446 
1447  return $text;
1448  }
1449 
1450  private function wellFormed( $text ) {
1451  $html =
1453  '<html>' .
1454  $text .
1455  '</html>';
1456 
1457  $parser = xml_parser_create( "UTF-8" );
1458 
1459  # case folding violates XML standard, turn it off
1460  xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
1461 
1462  if ( !xml_parse( $parser, $html, true ) ) {
1463  $err = xml_error_string( xml_get_error_code( $parser ) );
1464  $position = xml_get_current_byte_index( $parser );
1465  $fragment = $this->extractFragment( $html, $position );
1466  $this->mXmlError = "$err at byte $position:\n$fragment";
1467  xml_parser_free( $parser );
1468 
1469  return false;
1470  }
1471 
1472  xml_parser_free( $parser );
1473 
1474  return true;
1475  }
1476 
1477  private function extractFragment( $text, $position ) {
1478  $start = max( 0, $position - 10 );
1479  $before = $position - $start;
1480  $fragment = '...' .
1481  $this->term->color( 34 ) .
1482  substr( $text, $start, $before ) .
1483  $this->term->color( 0 ) .
1484  $this->term->color( 31 ) .
1485  $this->term->color( 1 ) .
1486  substr( $text, $position, 1 ) .
1487  $this->term->color( 0 ) .
1488  $this->term->color( 34 ) .
1489  substr( $text, $position + 1, 9 ) .
1490  $this->term->color( 0 ) .
1491  '...';
1492  $display = str_replace( "\n", ' ', $fragment );
1493  $caret = ' ' .
1494  str_repeat( ' ', $before ) .
1495  $this->term->color( 31 ) .
1496  '^' .
1497  $this->term->color( 0 );
1498 
1499  return "$display\n$caret";
1500  }
1501 
1502  static function getFakeTimestamp( &$parser, &$ts ) {
1503  $ts = 123; //parsed as '1970-01-01T00:02:03Z'
1504  return true;
1505  }
1506 }
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:3477
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