MediaWiki  1.32.5
Cite.php
Go to the documentation of this file.
1 <?php
2 
28 
33 class Cite {
34 
38  const DEFAULT_GROUP = '';
39 
44  const MAX_STORAGE_LENGTH = 65535; // Size of MySQL 'blob' field
45 
49  const EXT_DATA_KEY = 'Cite:References';
50 
54  const DATA_VERSION_NUMBER = 1;
55 
59  const CACHE_DURATION_ONPARSE = 3600; // 1 hour
60 
64  const CACHE_DURATION_ONFETCH = 18000; // 5 hours
65 
97  private $mRefs = [];
98 
104  private $mOutCnt = 0;
105 
109  private $mGroupCnt = [];
110 
117  private $mCallCnt = 0;
118 
126  private $mBacklinkLabels;
127 
133  private $mLinkLabels = [];
134 
138  private $mParser;
139 
146  private $mHaveAfterParse = false;
147 
154  public $mInCite = false;
155 
162  public $mInReferences = false;
163 
169  private $mReferencesErrors = [];
170 
176  private $mReferencesGroup = '';
177 
185  private $mRefCallStack = [];
186 
190  private $mBumpRefData = false;
191 
196  private static $hooksInstalled = false;
197 
208  public function ref( $str, array $argv, Parser $parser, PPFrame $frame ) {
209  if ( $this->mInCite ) {
210  return htmlspecialchars( "<ref>$str</ref>" );
211  }
212 
213  $this->mCallCnt++;
214  $this->mInCite = true;
215 
216  $ret = $this->guardedRef( $str, $argv, $parser );
217 
218  $this->mInCite = false;
219 
220  $parserOutput = $parser->getOutput();
221  $parserOutput->addModules( 'ext.cite.a11y' );
222  $parserOutput->addModuleStyles( 'ext.cite.styles' );
223 
224  $frame->setVolatile();
225 
226  // new <ref> tag, we may need to bump the ref data counter
227  // to avoid overwriting a previous group
228  $this->mBumpRefData = true;
229 
230  return $ret;
231  }
232 
242  private function guardedRef(
243  $str,
244  array $argv,
245  Parser $parser,
246  $default_group = self::DEFAULT_GROUP
247  ) {
248  $this->mParser = $parser;
249 
250  # The key here is the "name" attribute.
251  list( $key, $group, $follow, $dir ) = $this->refArg( $argv );
252  // empty string indicate invalid dir
253  if ( $dir === '' && $str !== '' ) {
254  $str .= $this->plainError( 'cite_error_ref_invalid_dir', $argv['dir'] );
255  }
256  # Split these into groups.
257  if ( $group === null ) {
258  if ( $this->mInReferences ) {
259  $group = $this->mReferencesGroup;
260  } else {
261  $group = $default_group;
262  }
263  }
264 
265  /*
266  * This section deals with constructions of the form
267  *
268  * <references>
269  * <ref name="foo"> BAR </ref>
270  * </references>
271  */
272  if ( $this->mInReferences ) {
273  $isSectionPreview = $parser->getOptions()->getIsSectionPreview();
274  if ( $group != $this->mReferencesGroup ) {
275  # <ref> and <references> have conflicting group attributes.
276  $this->mReferencesErrors[] =
277  $this->error(
278  'cite_error_references_group_mismatch',
279  Sanitizer::safeEncodeAttribute( $group )
280  );
281  } elseif ( $str !== '' ) {
282  if ( !$isSectionPreview && !isset( $this->mRefs[$group] ) ) {
283  # Called with group attribute not defined in text.
284  $this->mReferencesErrors[] =
285  $this->error(
286  'cite_error_references_missing_group',
287  Sanitizer::safeEncodeAttribute( $group )
288  );
289  } elseif ( $key === null || $key === '' ) {
290  # <ref> calls inside <references> must be named
291  $this->mReferencesErrors[] =
292  $this->error( 'cite_error_references_no_key' );
293  } elseif ( !$isSectionPreview && !isset( $this->mRefs[$group][$key] ) ) {
294  # Called with name attribute not defined in text.
295  $this->mReferencesErrors[] =
296  $this->error( 'cite_error_references_missing_key', Sanitizer::safeEncodeAttribute( $key ) );
297  } else {
298  if (
299  isset( $this->mRefs[$group][$key]['text'] ) &&
300  $str !== $this->mRefs[$group][$key]['text']
301  ) {
302  // two refs with same key and different content
303  // add error message to the original ref
304  $this->mRefs[$group][$key]['text'] .= ' ' . $this->plainError(
305  'cite_error_references_duplicate_key', $key
306  );
307  } else {
308  # Assign the text to corresponding ref
309  $this->mRefs[$group][$key]['text'] = $str;
310  }
311  }
312  } else {
313  # <ref> called in <references> has no content.
314  $this->mReferencesErrors[] =
315  $this->error( 'cite_error_empty_references_define', Sanitizer::safeEncodeAttribute( $key ) );
316  }
317  return '';
318  }
319 
320  if ( $str === '' ) {
321  # <ref ...></ref>. This construct is invalid if
322  # it's a contentful ref, but OK if it's a named duplicate and should
323  # be equivalent <ref ... />, for compatability with #tag.
324  if ( is_string( $key ) && $key !== '' ) {
325  $str = null;
326  } else {
327  $this->mRefCallStack[] = false;
328 
329  return $this->error( 'cite_error_ref_no_input' );
330  }
331  }
332 
333  if ( $key === false ) {
334  # TODO: Comment this case; what does this condition mean?
335  $this->mRefCallStack[] = false;
336  return $this->error( 'cite_error_ref_too_many_keys' );
337  }
338 
339  if ( $str === null && $key === null ) {
340  # Something like <ref />; this makes no sense.
341  $this->mRefCallStack[] = false;
342  return $this->error( 'cite_error_ref_no_key' );
343  }
344 
345  if ( is_string( $key ) && preg_match( '/^[0-9]+$/', $key ) ||
346  is_string( $follow ) && preg_match( '/^[0-9]+$/', $follow )
347  ) {
348  # Numeric names mess up the resulting id's, potentially produ-
349  # cing duplicate id's in the XHTML. The Right Thing To Do
350  # would be to mangle them, but it's not really high-priority
351  # (and would produce weird id's anyway).
352 
353  $this->mRefCallStack[] = false;
354  return $this->error( 'cite_error_ref_numeric_key' );
355  }
356 
357  if ( preg_match(
358  '/<ref\b[^<]*?>/',
359  preg_replace( '#<([^ ]+?).*?>.*?</\\1 *>|<!--.*?-->#', '', $str )
360  ) ) {
361  # (bug T8199) This most likely implies that someone left off the
362  # closing </ref> tag, which will cause the entire article to be
363  # eaten up until the next <ref>. So we bail out early instead.
364  # The fancy regex above first tries chopping out anything that
365  # looks like a comment or SGML tag, which is a crude way to avoid
366  # false alarms for <nowiki>, <pre>, etc.
367 
368  # Possible improvement: print the warning, followed by the contents
369  # of the <ref> tag. This way no part of the article will be eaten
370  # even temporarily.
371 
372  $this->mRefCallStack[] = false;
373  return $this->error( 'cite_error_included_ref' );
374  }
375 
376  if ( is_string( $key ) || is_string( $str ) ) {
377  # We don't care about the content: if the key exists, the ref
378  # is presumptively valid. Either it stores a new ref, or re-
379  # fers to an existing one. If it refers to a nonexistent ref,
380  # we'll figure that out later. Likewise it's definitely valid
381  # if there's any content, regardless of key.
382 
383  return $this->stack( $str, $key, $group, $follow, $argv, $dir, $parser );
384  }
385 
386  # Not clear how we could get here, but something is probably
387  # wrong with the types. Let's fail fast.
388  throw new Exception( 'Invalid $str and/or $key: ' . serialize( [ $str, $key ] ) );
389  }
390 
404  private function refArg( array $argv ) {
405  $cnt = count( $argv );
406  $group = null;
407  $key = null;
408  $follow = null;
409  $dir = null;
410  if ( isset( $argv['dir'] ) ) {
411  // compare the dir attribute value against an explicit whitelist.
412  $dir = '';
413  $isValidDir = in_array( strtolower( $argv['dir'] ), [ 'ltr', 'rtl' ] );
414  if ( $isValidDir ) {
415  $dir = Html::expandAttributes( [ 'class' => 'mw-cite-dir-' . strtolower( $argv['dir'] ) ] );
416  }
417 
418  unset( $argv['dir'] );
419  --$cnt;
420  }
421  if ( $cnt > 2 ) {
422  // There should only be one key or follow parameter, and one group parameter
423  // FIXME : this looks inconsistent, it should probably return a tuple
424  return false;
425  } elseif ( $cnt >= 1 ) {
426  if ( isset( $argv['name'] ) && isset( $argv['follow'] ) ) {
427  return [ false, false, false, false ];
428  }
429  if ( isset( $argv['name'] ) ) {
430  // Key given.
431  $key = trim( $argv['name'] );
432  unset( $argv['name'] );
433  --$cnt;
434  }
435  if ( isset( $argv['follow'] ) ) {
436  // Follow given.
437  $follow = trim( $argv['follow'] );
438  unset( $argv['follow'] );
439  --$cnt;
440  }
441  if ( isset( $argv['group'] ) ) {
442  // Group given.
443  $group = $argv['group'];
444  unset( $argv['group'] );
445  --$cnt;
446  }
447 
448  if ( $cnt === 0 ) {
449  return [ $key, $group, $follow, $dir ];
450  } else {
451  // Invalid key
452  return [ false, false, false, false ];
453  }
454  } else {
455  // No key
456  return [ null, $group, false, $dir ];
457  }
458  }
459 
474  private function stack( $str, $key, $group, $follow, array $call, $dir, Parser $parser ) {
475  if ( !isset( $this->mRefs[$group] ) ) {
476  $this->mRefs[$group] = [];
477  }
478  if ( !isset( $this->mGroupCnt[$group] ) ) {
479  $this->mGroupCnt[$group] = 0;
480  }
481  if ( $follow != null ) {
482  if ( isset( $this->mRefs[$group][$follow] ) && is_array( $this->mRefs[$group][$follow] ) ) {
483  // add text to the note that is being followed
484  $this->mRefs[$group][$follow]['text'] .= ' ' . $str;
485  } else {
486  // insert part of note at the beginning of the group
487  $groupsCount = count( $this->mRefs[$group] );
488  for ( $k = 0; $k < $groupsCount; $k++ ) {
489  if ( !isset( $this->mRefs[$group][$k]['follow'] ) ) {
490  break;
491  }
492  }
493  array_splice( $this->mRefs[$group], $k, 0, [ [
494  'count' => -1,
495  'text' => $str,
496  'key' => ++$this->mOutCnt,
497  'follow' => $follow,
498  'dir' => $dir
499  ] ] );
500  array_splice( $this->mRefCallStack, $k, 0,
501  [ [ 'new', $call, $str, $key, $group, $this->mOutCnt ] ] );
502  }
503  // return an empty string : this is not a reference
504  return '';
505  }
506 
507  if ( $key === null ) {
508  // No key
509  // $this->mRefs[$group][] = $str;
510 
511  $this->mRefs[$group][] = [
512  'count' => -1,
513  'text' => $str,
514  'key' => ++$this->mOutCnt,
515  'dir' => $dir
516  ];
517  $this->mRefCallStack[] = [ 'new', $call, $str, $key, $group, $this->mOutCnt ];
518 
519  return $this->linkRef( $group, $this->mOutCnt );
520  }
521  if ( !is_string( $key ) ) {
522  throw new Exception( 'Invalid stack key: ' . serialize( $key ) );
523  }
524 
525  // Valid key
526  if ( !isset( $this->mRefs[$group][$key] ) || !is_array( $this->mRefs[$group][$key] ) ) {
527  // First occurrence
528  $this->mRefs[$group][$key] = [
529  'text' => $str,
530  'count' => 0,
531  'key' => ++$this->mOutCnt,
532  'number' => ++$this->mGroupCnt[$group],
533  'dir' => $dir
534  ];
535  $this->mRefCallStack[] = [ 'new', $call, $str, $key, $group, $this->mOutCnt ];
536 
537  return $this->linkRef(
538  $group,
539  $key,
540  $this->mRefs[$group][$key]['key'] . "-" . $this->mRefs[$group][$key]['count'],
541  $this->mRefs[$group][$key]['number'],
542  "-" . $this->mRefs[$group][$key]['key']
543  );
544  }
545 
546  // We've been here before
547  if ( $this->mRefs[$group][$key]['text'] === null && $str !== '' ) {
548  // If no text found before, use this text
549  $this->mRefs[$group][$key]['text'] = $str;
550  // Use the dir parameter only from the full definition of a named ref tag
551  $this->mRefs[$group][$key]['dir'] = $dir;
552  $this->mRefCallStack[] = [ 'assign', $call, $str, $key, $group,
553  $this->mRefs[$group][$key]['key'] ];
554  } else {
555  if ( $str != null && $str !== ''
556  // T205803 different strip markers might hide the same text
557  && $parser->mStripState->unstripBoth( $str )
558  !== $parser->mStripState->unstripBoth( $this->mRefs[$group][$key]['text'] )
559  ) {
560  // two refs with same key and different content
561  // add error message to the original ref
562  $this->mRefs[$group][$key]['text'] .= ' ' . $this->plainError(
563  'cite_error_references_duplicate_key', $key
564  );
565  }
566  $this->mRefCallStack[] = [ 'increment', $call, $str, $key, $group,
567  $this->mRefs[$group][$key]['key'] ];
568  }
569  return $this->linkRef(
570  $group,
571  $key,
572  $this->mRefs[$group][$key]['key'] . "-" . ++$this->mRefs[$group][$key]['count'],
573  $this->mRefs[$group][$key]['number'],
574  "-" . $this->mRefs[$group][$key]['key']
575  );
576  }
577 
599  private function rollbackRef( $type, $key, $group, $index ) {
600  if ( !isset( $this->mRefs[$group] ) ) {
601  return;
602  }
603 
604  if ( $key === null ) {
605  foreach ( $this->mRefs[$group] as $k => $v ) {
606  if ( $this->mRefs[$group][$k]['key'] === $index ) {
607  $key = $k;
608  break;
609  }
610  }
611  }
612 
613  // Sanity checks that specified element exists.
614  if ( $key === null ) {
615  return;
616  }
617  if ( !isset( $this->mRefs[$group][$key] ) ) {
618  return;
619  }
620  if ( $this->mRefs[$group][$key]['key'] != $index ) {
621  return;
622  }
623 
624  switch ( $type ) {
625  case 'new':
626  # Rollback the addition of new elements to the stack.
627  unset( $this->mRefs[$group][$key] );
628  if ( $this->mRefs[$group] === [] ) {
629  unset( $this->mRefs[$group] );
630  unset( $this->mGroupCnt[$group] );
631  }
632  break;
633  case 'assign':
634  # Rollback assignment of text to pre-existing elements.
635  $this->mRefs[$group][$key]['text'] = null;
636  # continue without break
637  case 'increment':
638  # Rollback increase in named ref occurrences.
639  $this->mRefs[$group][$key]['count']--;
640  break;
641  }
642  }
643 
654  public function references( $str, array $argv, Parser $parser, PPFrame $frame ) {
655  if ( $this->mInCite || $this->mInReferences ) {
656  if ( is_null( $str ) ) {
657  return htmlspecialchars( "<references/>" );
658  }
659  return htmlspecialchars( "<references>$str</references>" );
660  }
661  $this->mCallCnt++;
662  $this->mInReferences = true;
663  $ret = $this->guardedReferences( $str, $argv, $parser );
664  $this->mInReferences = false;
665  $frame->setVolatile();
666  return $ret;
667  }
668 
678  private function guardedReferences(
679  $str,
680  array $argv,
681  Parser $parser,
682  $group = self::DEFAULT_GROUP
683  ) {
684  global $wgCiteResponsiveReferences;
685 
686  $this->mParser = $parser;
687 
688  if ( isset( $argv['group'] ) ) {
689  $group = $argv['group'];
690  unset( $argv['group'] );
691  }
692 
693  if ( strval( $str ) !== '' ) {
694  $this->mReferencesGroup = $group;
695 
696  # Detect whether we were sent already rendered <ref>s.
697  # Mostly a side effect of using #tag to call references.
698  # The following assumes that the parsed <ref>s sent within
699  # the <references> block were the most recent calls to
700  # <ref>. This assumption is true for all known use cases,
701  # but not strictly enforced by the parser. It is possible
702  # that some unusual combination of #tag, <references> and
703  # conditional parser functions could be created that would
704  # lead to malformed references here.
705  $count = substr_count( $str, Parser::MARKER_PREFIX . "-ref-" );
706  $redoStack = [];
707 
708  # Undo effects of calling <ref> while unaware of containing <references>
709  for ( $i = 1; $i <= $count; $i++ ) {
710  if ( !$this->mRefCallStack ) {
711  break;
712  }
713 
714  $call = array_pop( $this->mRefCallStack );
715  $redoStack[] = $call;
716  if ( $call !== false ) {
717  list( $type, $ref_argv, $ref_str,
718  $ref_key, $ref_group, $ref_index ) = $call;
719  $this->rollbackRef( $type, $ref_key, $ref_group, $ref_index );
720  }
721  }
722 
723  # Rerun <ref> call now that mInReferences is set.
724  for ( $i = count( $redoStack ) - 1; $i >= 0; $i-- ) {
725  $call = $redoStack[$i];
726  if ( $call !== false ) {
727  list( $type, $ref_argv, $ref_str,
728  $ref_key, $ref_group, $ref_index ) = $call;
729  $this->guardedRef( $ref_str, $ref_argv, $parser );
730  }
731  }
732 
733  # Parse $str to process any unparsed <ref> tags.
734  $parser->recursiveTagParse( $str );
735 
736  # Reset call stack
737  $this->mRefCallStack = [];
738  }
739 
740  if ( isset( $argv['responsive'] ) ) {
741  $responsive = $argv['responsive'] !== '0';
742  unset( $argv['responsive'] );
743  } else {
744  $responsive = $wgCiteResponsiveReferences;
745  }
746 
747  // There are remaining parameters we don't recognise
748  if ( $argv ) {
749  return $this->error( 'cite_error_references_invalid_parameters' );
750  }
751 
752  $s = $this->referencesFormat( $group, $responsive );
753 
754  # Append errors generated while processing <references>
755  if ( $this->mReferencesErrors ) {
756  $s .= "\n" . implode( "<br />\n", $this->mReferencesErrors );
757  $this->mReferencesErrors = [];
758  }
759  return $s;
760  }
761 
772  private function referencesFormat( $group, $responsive ) {
773  if ( !$this->mRefs || !isset( $this->mRefs[$group] ) ) {
774  return '';
775  }
776 
777  $ent = [];
778  foreach ( $this->mRefs[$group] as $k => $v ) {
779  $ent[] = $this->referencesFormatEntry( $k, $v );
780  }
781 
782  // Add new lines between the list items (ref entires) to avoid confusing tidy (T15073).
783  // Note: This builds a string of wikitext, not html.
784  $parserInput = Html::rawElement( 'ol', [ 'class' => [ 'references' ] ],
785  "\n" . implode( "\n", $ent ) . "\n"
786  );
787 
788  // Live hack: parse() adds two newlines on WM, can't reproduce it locally -ævar
789  $ret = rtrim( $this->mParser->recursiveTagParse( $parserInput ), "\n" );
790 
791  if ( $responsive ) {
792  // Use a DIV wrap because column-count on a list directly is broken in Chrome.
793  // See https://bugs.chromium.org/p/chromium/issues/detail?id=498730.
794  $wrapClasses = [ 'mw-references-wrap' ];
795  if ( count( $this->mRefs[$group] ) > 10 ) {
796  $wrapClasses[] = 'mw-references-columns';
797  }
798  $ret = Html::rawElement( 'div', [ 'class' => $wrapClasses ], $ret );
799  }
800 
801  if ( !$this->mParser->getOptions()->getIsPreview() ) {
802  // save references data for later use by LinksUpdate hooks
803  $this->saveReferencesData( $group );
804  }
805 
806  // done, clean up so we can reuse the group
807  unset( $this->mRefs[$group] );
808  unset( $this->mGroupCnt[$group] );
809 
810  return $ret;
811  }
812 
821  private function referencesFormatEntry( $key, $val ) {
822  // Anonymous reference
823  if ( !is_array( $val ) ) {
824  return wfMessage(
825  'cite_references_link_one',
826  $this->normalizeKey(
827  self::getReferencesKey( $key )
828  ),
829  $this->normalizeKey(
830  $this->refKey( $key )
831  ),
832  $this->referenceText( $key, $val ),
833  $val['dir']
834  )->inContentLanguage()->plain();
835  }
836  $text = $this->referenceText( $key, $val['text'] );
837  if ( isset( $val['follow'] ) ) {
838  return wfMessage(
839  'cite_references_no_link',
840  $this->normalizeKey(
841  self::getReferencesKey( $val['follow'] )
842  ),
843  $text
844  )->inContentLanguage()->plain();
845  }
846  if ( !isset( $val['count'] ) ) {
847  // this handles the case of section preview for list-defined references
848  return wfMessage( 'cite_references_link_many',
849  $this->normalizeKey(
850  self::getReferencesKey( $key . "-" . ( isset( $val['key'] ) ? $val['key'] : '' ) )
851  ),
852  '',
853  $text
854  )->inContentLanguage()->plain();
855  }
856  if ( $val['count'] < 0 ) {
857  return wfMessage(
858  'cite_references_link_one',
859  $this->normalizeKey(
860  self::getReferencesKey( $val['key'] )
861  ),
862  $this->normalizeKey(
863  # $this->refKey( $val['key'], $val['count'] )
864  $this->refKey( $val['key'] )
865  ),
866  $text,
867  $val['dir']
868  )->inContentLanguage()->plain();
869  // Standalone named reference, I want to format this like an
870  // anonymous reference because displaying "1. 1.1 Ref text" is
871  // overkill and users frequently use named references when they
872  // don't need them for convenience
873  }
874  if ( $val['count'] === 0 ) {
875  return wfMessage(
876  'cite_references_link_one',
877  $this->normalizeKey(
878  self::getReferencesKey( $key . "-" . $val['key'] )
879  ),
880  $this->normalizeKey(
881  # $this->refKey( $key, $val['count'] ),
882  $this->refKey( $key, $val['key'] . "-" . $val['count'] )
883  ),
884  $text,
885  $val['dir']
886  )->inContentLanguage()->plain();
887  // Named references with >1 occurrences
888  }
889  $links = [];
890  // for group handling, we have an extra key here.
891  for ( $i = 0; $i <= $val['count']; ++$i ) {
892  $links[] = wfMessage(
893  'cite_references_link_many_format',
894  $this->normalizeKey(
895  $this->refKey( $key, $val['key'] . "-$i" )
896  ),
897  $this->referencesFormatEntryNumericBacklinkLabel( $val['number'], $i, $val['count'] ),
898  $this->referencesFormatEntryAlternateBacklinkLabel( $i )
899  )->inContentLanguage()->plain();
900  }
901 
902  $list = $this->listToText( $links );
903 
904  return wfMessage( 'cite_references_link_many',
905  $this->normalizeKey(
906  self::getReferencesKey( $key . "-" . $val['key'] )
907  ),
908  $list,
909  $text,
910  $val['dir']
911  )->inContentLanguage()->plain();
912  }
913 
920  private function referenceText( $key, $text ) {
921  if ( !isset( $text ) || $text === '' ) {
922  if ( $this->mParser->getOptions()->getIsSectionPreview() ) {
923  return $this->warning( 'cite_warning_sectionpreview_no_text', $key, 'noparse' );
924  }
925  return $this->plainError( 'cite_error_references_no_text', $key );
926  }
927  return '<span class="reference-text">' . rtrim( $text, "\n" ) . "</span>\n";
928  }
929 
940  private function referencesFormatEntryNumericBacklinkLabel( $base, $offset, $max ) {
941  global $wgContLang;
942  $scope = strlen( $max );
943  $ret = $wgContLang->formatNum(
944  sprintf( "%s.%0{$scope}s", $base, $offset )
945  );
946  return $ret;
947  }
948 
959  private function referencesFormatEntryAlternateBacklinkLabel( $offset ) {
960  if ( !isset( $this->mBacklinkLabels ) ) {
961  $this->genBacklinkLabels();
962  }
963  if ( isset( $this->mBacklinkLabels[$offset] ) ) {
964  return $this->mBacklinkLabels[$offset];
965  } else {
966  // Feed me!
967  return $this->plainError( 'cite_error_references_no_backlink_label', null );
968  }
969  }
970 
983  private function getLinkLabel( $offset, $group, $label ) {
984  $message = "cite_link_label_group-$group";
985  if ( !isset( $this->mLinkLabels[$group] ) ) {
986  $this->genLinkLabels( $group, $message );
987  }
988  if ( $this->mLinkLabels[$group] === false ) {
989  // Use normal representation, ie. "$group 1", "$group 2"...
990  return $label;
991  }
992 
993  if ( isset( $this->mLinkLabels[$group][$offset - 1] ) ) {
994  return $this->mLinkLabels[$group][$offset - 1];
995  } else {
996  // Feed me!
997  return $this->plainError( 'cite_error_no_link_label_group', [ $group, $message ] );
998  }
999  }
1000 
1010  private function refKey( $key, $num = null ) {
1011  $prefix = wfMessage( 'cite_reference_link_prefix' )->inContentLanguage()->text();
1012  $suffix = wfMessage( 'cite_reference_link_suffix' )->inContentLanguage()->text();
1013  if ( isset( $num ) ) {
1014  $key = wfMessage( 'cite_reference_link_key_with_num', $key, $num )
1015  ->inContentLanguage()->plain();
1016  }
1017 
1018  return "$prefix$key$suffix";
1019  }
1020 
1029  public static function getReferencesKey( $key ) {
1030  $prefix = wfMessage( 'cite_references_link_prefix' )->inContentLanguage()->text();
1031  $suffix = wfMessage( 'cite_references_link_suffix' )->inContentLanguage()->text();
1032 
1033  return "$prefix$key$suffix";
1034  }
1035 
1052  private function linkRef( $group, $key, $count = null, $label = null, $subkey = '' ) {
1053  global $wgContLang;
1054  $label = is_null( $label ) ? ++$this->mGroupCnt[$group] : $label;
1055 
1056  return $this->mParser->recursiveTagParse(
1057  wfMessage(
1058  'cite_reference_link',
1059  $this->normalizeKey(
1060  $this->refKey( $key, $count )
1061  ),
1062  $this->normalizeKey(
1063  self::getReferencesKey( $key . $subkey )
1064  ),
1065  Sanitizer::safeEncodeAttribute(
1066  $this->getLinkLabel( $label, $group,
1067  ( ( $group === self::DEFAULT_GROUP ) ? '' : "$group " ) . $wgContLang->formatNum( $label ) )
1068  )
1069  )->inContentLanguage()->plain()
1070  );
1071  }
1072 
1079  private function normalizeKey( $key ) {
1080  $key = Sanitizer::escapeIdForAttribute( $key );
1081  $key = Sanitizer::safeEncodeAttribute( $key );
1082 
1083  return $key;
1084  }
1085 
1096  private function listToText( $arr ) {
1097  $cnt = count( $arr );
1098 
1099  $sep = wfMessage( 'cite_references_link_many_sep' )->inContentLanguage()->plain();
1100  $and = wfMessage( 'cite_references_link_many_and' )->inContentLanguage()->plain();
1101 
1102  if ( $cnt === 1 ) {
1103  // Enforce always returning a string
1104  return (string)$arr[0];
1105  } else {
1106  $t = array_slice( $arr, 0, $cnt - 1 );
1107  return implode( $sep, $t ) . $and . $arr[$cnt - 1];
1108  }
1109  }
1110 
1116  private function genBacklinkLabels() {
1117  $text = wfMessage( 'cite_references_link_many_format_backlink_labels' )
1118  ->inContentLanguage()->plain();
1119  $this->mBacklinkLabels = preg_split( '#[\n\t ]#', $text );
1120  }
1121 
1130  private function genLinkLabels( $group, $message ) {
1131  $text = false;
1132  $msg = wfMessage( $message )->inContentLanguage();
1133  if ( $msg->exists() ) {
1134  $text = $msg->plain();
1135  }
1136  $this->mLinkLabels[$group] = ( !$text ) ? false : preg_split( '#[\n\t ]#', $text );
1137  }
1138 
1145  public function clearState( Parser $parser ) {
1146  if ( $parser->extCite !== $this ) {
1147  $parser->extCite->clearState( $parser );
1148  return;
1149  }
1150 
1151  # Don't clear state when we're in the middle of parsing
1152  # a <ref> tag
1153  if ( $this->mInCite || $this->mInReferences ) {
1154  return;
1155  }
1156 
1157  $this->mGroupCnt = [];
1158  $this->mOutCnt = 0;
1159  $this->mCallCnt = 0;
1160  $this->mRefs = [];
1161  $this->mReferencesErrors = [];
1162  $this->mRefCallStack = [];
1163  }
1164 
1170  public function cloneState( Parser $parser ) {
1171  if ( $parser->extCite !== $this ) {
1172  $parser->extCite->cloneState( $parser );
1173  return;
1174  }
1175 
1176  $parser->extCite = clone $this;
1177  $parser->setHook( 'ref', [ $parser->extCite, 'ref' ] );
1178  $parser->setHook( 'references', [ $parser->extCite, 'references' ] );
1179 
1180  // Clear the state, making sure it will actually work.
1181  $parser->extCite->mInCite = false;
1182  $parser->extCite->mInReferences = false;
1183  $parser->extCite->clearState( $parser );
1184  }
1185 
1198  public function checkRefsNoReferences( $afterParse, $parser, &$text ) {
1199  global $wgCiteResponsiveReferences;
1200  if ( is_null( $parser->extCite ) ) {
1201  return;
1202  }
1203  if ( $parser->extCite !== $this ) {
1204  $parser->extCite->checkRefsNoReferences( $afterParse, $parser, $text );
1205  return;
1206  }
1207 
1208  if ( $afterParse ) {
1209  $this->mHaveAfterParse = true;
1210  } elseif ( $this->mHaveAfterParse ) {
1211  return;
1212  }
1213 
1214  if ( !$parser->getOptions()->getIsPreview() ) {
1215  // save references data for later use by LinksUpdate hooks
1216  if ( $this->mRefs && isset( $this->mRefs[self::DEFAULT_GROUP] ) ) {
1217  $this->saveReferencesData();
1218  }
1219  $isSectionPreview = false;
1220  } else {
1221  $isSectionPreview = $parser->getOptions()->getIsSectionPreview();
1222  }
1223 
1224  $s = '';
1225  foreach ( $this->mRefs as $group => $refs ) {
1226  if ( !$refs ) {
1227  continue;
1228  }
1229  if ( $group === self::DEFAULT_GROUP || $isSectionPreview ) {
1230  $this->mInReferences = true;
1231  $s .= $this->referencesFormat( $group, $wgCiteResponsiveReferences );
1232  $this->mInReferences = false;
1233  } else {
1234  $s .= "\n<br />" .
1235  $this->error(
1236  'cite_error_group_refs_without_references',
1237  Sanitizer::safeEncodeAttribute( $group )
1238  );
1239  }
1240  }
1241  if ( $isSectionPreview && $s !== '' ) {
1242  // provide a preview of references in its own section
1243  $text .= "\n" . '<div class="mw-ext-cite-cite_section_preview_references" >';
1244  $headerMsg = wfMessage( 'cite_section_preview_references' );
1245  if ( !$headerMsg->isDisabled() ) {
1246  $text .= '<h2 id="mw-ext-cite-cite_section_preview_references_header" >'
1247  . $headerMsg->escaped()
1248  . '</h2>';
1249  }
1250  $text .= $s . '</div>';
1251  } else {
1252  $text .= $s;
1253  }
1254  }
1255 
1263  private function saveReferencesData( $group = self::DEFAULT_GROUP ) {
1264  global $wgCiteStoreReferencesData;
1265  if ( !$wgCiteStoreReferencesData ) {
1266  return;
1267  }
1268  $savedRefs = $this->mParser->getOutput()->getExtensionData( self::EXT_DATA_KEY );
1269  if ( $savedRefs === null ) {
1270  // Initialize array structure
1271  $savedRefs = [
1272  'refs' => [],
1273  'version' => self::DATA_VERSION_NUMBER,
1274  ];
1275  }
1276  if ( $this->mBumpRefData ) {
1277  // This handles pages with multiple <references/> tags with <ref> tags in between.
1278  // On those, a group can appear several times, so we need to avoid overwriting
1279  // a previous appearance.
1280  $savedRefs['refs'][] = [];
1281  $this->mBumpRefData = false;
1282  }
1283  $n = count( $savedRefs['refs'] ) - 1;
1284  // save group
1285  $savedRefs['refs'][$n][$group] = $this->mRefs[$group];
1286 
1287  $this->mParser->getOutput()->setExtensionData( self::EXT_DATA_KEY, $savedRefs );
1288  }
1289 
1295  public static function setHooks( Parser $parser ) {
1296  global $wgHooks;
1297 
1298  $parser->extCite = new self();
1299 
1300  if ( !self::$hooksInstalled ) {
1301  $wgHooks['ParserClearState'][] = [ $parser->extCite, 'clearState' ];
1302  $wgHooks['ParserCloned'][] = [ $parser->extCite, 'cloneState' ];
1303  $wgHooks['ParserAfterParse'][] = [ $parser->extCite, 'checkRefsNoReferences', true ];
1304  $wgHooks['ParserBeforeTidy'][] = [ $parser->extCite, 'checkRefsNoReferences', false ];
1305  self::$hooksInstalled = true;
1306  }
1307  $parser->setHook( 'ref', [ $parser->extCite, 'ref' ] );
1308  $parser->setHook( 'references', [ $parser->extCite, 'references' ] );
1309  }
1310 
1318  private function error( $key, $param = null ) {
1319  $error = $this->plainError( $key, $param );
1320  return $this->mParser->recursiveTagParse( $error );
1321  }
1322 
1331  private function plainError( $key, $param = null ) {
1332  # For ease of debugging and because errors are rare, we
1333  # use the user language and split the parser cache.
1334  $lang = $this->mParser->getOptions()->getUserLangObj();
1335  $dir = $lang->getDir();
1336 
1337  # We rely on the fact that PHP is okay with passing unused argu-
1338  # ments to functions. If $1 is not used in the message, wfMessage will
1339  # just ignore the extra parameter.
1340  $msg = wfMessage(
1341  'cite_error',
1342  wfMessage( $key, $param )->inLanguage( $lang )->plain()
1343  )
1344  ->inLanguage( $lang )
1345  ->plain();
1346 
1347  $this->mParser->addTrackingCategory( 'cite-tracking-category-cite-error' );
1348 
1350  'span',
1351  [
1352  'class' => 'error mw-ext-cite-error',
1353  'lang' => $lang->getHtmlCode(),
1354  'dir' => $dir,
1355  ],
1356  $msg
1357  );
1358 
1359  return $ret;
1360  }
1361 
1370  private function warning( $key, $param = null, $parse = 'parse' ) {
1371  # For ease of debugging and because errors are rare, we
1372  # use the user language and split the parser cache.
1373  $lang = $this->mParser->getOptions()->getUserLangObj();
1374  $dir = $lang->getDir();
1375 
1376  # We rely on the fact that PHP is okay with passing unused argu-
1377  # ments to functions. If $1 is not used in the message, wfMessage will
1378  # just ignore the extra parameter.
1379  $msg = wfMessage(
1380  'cite_warning',
1381  wfMessage( $key, $param )->inLanguage( $lang )->plain()
1382  )
1383  ->inLanguage( $lang )
1384  ->plain();
1385 
1386  $key = preg_replace( '/^cite_warning_/', '', $key ) . '';
1388  'span',
1389  [
1390  'class' => 'warning mw-ext-cite-warning mw-ext-cite-warning-' .
1391  Sanitizer::escapeClass( $key ),
1392  'lang' => $lang->getHtmlCode(),
1393  'dir' => $dir,
1394  ],
1395  $msg
1396  );
1397 
1398  if ( $parse === 'parse' ) {
1399  $ret = $this->mParser->recursiveTagParse( $ret );
1400  }
1401 
1402  return $ret;
1403  }
1404 
1412  public static function getStoredReferences( Title $title ) {
1413  global $wgCiteStoreReferencesData;
1414  if ( !$wgCiteStoreReferencesData ) {
1415  return false;
1416  }
1417  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1418  $key = $cache->makeKey( self::EXT_DATA_KEY, $title->getArticleID() );
1419  return $cache->getWithSetCallback(
1420  $key,
1421  self::CACHE_DURATION_ONFETCH,
1422  function ( $oldValue, &$ttl, array &$setOpts ) use ( $title ) {
1423  $dbr = wfGetDB( DB_REPLICA );
1424  $setOpts += Database::getCacheSetOptions( $dbr );
1425  return self::recursiveFetchRefsFromDB( $title, $dbr );
1426  },
1427  [
1428  'checkKeys' => [ $key ],
1429  'lockTSE' => 30,
1430  ]
1431  );
1432  }
1433 
1445  private static function recursiveFetchRefsFromDB( Title $title, IDatabase $dbr,
1446  $string = '', $i = 1 ) {
1447  $id = $title->getArticleID();
1448  $result = $dbr->selectField(
1449  'page_props',
1450  'pp_value',
1451  [
1452  'pp_page' => $id,
1453  'pp_propname' => 'references-' . $i
1454  ],
1455  __METHOD__
1456  );
1457  if ( $result !== false ) {
1458  $string .= $result;
1459  $decodedString = gzdecode( $string );
1460  if ( $decodedString !== false ) {
1461  $json = json_decode( $decodedString, true );
1462  if ( json_last_error() === JSON_ERROR_NONE ) {
1463  return $json;
1464  }
1465  // corrupted json ?
1466  // shouldn't happen since when string is truncated, gzdecode should fail
1467  wfDebug( "Corrupted json detected when retrieving stored references for title id $id" );
1468  }
1469  // if gzdecode fails, try to fetch next references- property value
1470  return self::recursiveFetchRefsFromDB( $title, $dbr, $string, ++$i );
1471 
1472  } else {
1473  // no refs stored in page_props at this index
1474  if ( $i > 1 ) {
1475  // shouldn't happen
1476  wfDebug( "Failed to retrieve stored references for title id $id" );
1477  }
1478  return false;
1479  }
1480  }
1481 
1482 }
Wikimedia\Rdbms\Database
Relational database abstraction object.
Definition: Database.php:48
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:33
captcha-old.count
count
Definition: captcha-old.py:249
$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. 'ImgAuthModifyHeaders':Executed just before a file is streamed to a user via img_auth.php, allowing headers to be modified beforehand. $title:LinkTarget object & $headers:HTTP headers(name=> value, names are case insensitive). Two headers get special handling:If-Modified-Since(value must be a valid HTTP date) and Range(must be of the form "bytes=(\d*-\d*)") will be honored when streaming the file. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page. Return false to stop further processing of the tag $reader:XMLReader object & $pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUnknownUser':When a user doesn 't exist locally, this hook is called to give extensions an opportunity to auto-create it. If the auto-creation is successful, return false. $name:User name 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports. & $fullInterwikiPrefix:Interwiki prefix, may contain colons. & $pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable. Can be used to lazy-load the import sources list. & $importSources:The value of $wgImportSources. Modify as necessary. See the comment in DefaultSettings.php for the detail of how to structure this array. '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 '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 '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 '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. '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 IP::isTrustedProxy() & $ip:IP being check & $result:Change this value to override the result of IP::isTrustedProxy() '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 Sanitizer::validateEmail(), 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. 'LanguageGetMagic':DEPRECATED since 1.16! 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) '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 '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:Array with elements of the form "language:title" in the order that they will be output. & $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. 'LanguageSelector':Hook to change the language selector available on a page. $out:The output page. $cssClassName:CSS class name of the language selector. 'LinkBegin':DEPRECATED since 1.28! Use HtmlPageLinkRendererBegin instead. 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:2042
Html\expandAttributes
static expandAttributes(array $attribs)
Given an associative array of element attributes, generate a string to stick after the element name i...
Definition: Html.php:475
$s
$s
Definition: mergeMessageFileList.php:187
serialize
serialize()
Definition: ApiMessageTrait.php:131
$base
$base
Definition: generateLocalAutoload.php:11
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
$dbr
$dbr
Definition: testCompression.php:50
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:964
PPFrame\setVolatile
setVolatile( $flag=true)
Set the "volatile" flag.
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2693
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
$parser
see documentation in includes Linker php for Linker::makeImageLink or false for current used if you return false $parser
Definition: hooks.txt:1841
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
array
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
wfDebug
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:988
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
PPFrame
Definition: Preprocessor.php:166
$ret
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 & $ret
Definition: hooks.txt:2044
plain
either a plain
Definition: hooks.txt:2105
Title
Represents a title within MediaWiki.
Definition: Title.php:39
$cache
$cache
Definition: mcc.php:33
$wgHooks
$wgHooks['ArticleShow'][]
Definition: hooks.txt:108
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
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:210
true
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 true
Definition: hooks.txt:2044
error
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults error
Definition: hooks.txt:2683
$t
$t
Definition: testCompression.php:69
MediaWikiServices
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency MediaWikiServices
Definition: injection.txt:23
wfMessage
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation use $formDescriptor instead default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt
$wgContLang
$wgContLang
Definition: Setup.php:809
$type
$type
Definition: testCompression.php:48