MediaWiki  master
PPFrame_Hash.php
Go to the documentation of this file.
1 <?php
26 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
27 class PPFrame_Hash implements PPFrame {
28 
32  public $parser;
33 
37  public $preprocessor;
38 
42  public $title;
43  public $titleCache;
44 
50 
55  public $depth;
56 
57  private $volatile = false;
58  private $ttl = null;
59 
64 
68  public function __construct( $preprocessor ) {
69  $this->preprocessor = $preprocessor;
70  $this->parser = $preprocessor->parser;
71  $this->title = $this->parser->getTitle();
72  $this->titleCache = [ $this->title ? $this->title->getPrefixedDBkey() : false ];
73  $this->loopCheckHash = [];
74  $this->depth = 0;
75  $this->childExpansionCache = [];
76  }
77 
88  public function newChild( $args = false, $title = false, $indexOffset = 0 ) {
89  $namedArgs = [];
90  $numberedArgs = [];
91  if ( $title === false ) {
93  }
94  if ( $args !== false ) {
95  if ( $args instanceof PPNode_Hash_Array ) {
96  $args = $args->value;
97  } elseif ( !is_array( $args ) ) {
98  throw new MWException( __METHOD__ . ': $args must be array or PPNode_Hash_Array' );
99  }
100  foreach ( $args as $arg ) {
101  $bits = $arg->splitArg();
102  if ( $bits['index'] !== '' ) {
103  // Numbered parameter
104  $index = $bits['index'] - $indexOffset;
105  if ( isset( $namedArgs[$index] ) || isset( $numberedArgs[$index] ) ) {
106  $this->parser->getOutput()->addWarning( wfMessage( 'duplicate-args-warning',
107  wfEscapeWikiText( $this->title ),
109  wfEscapeWikiText( $index ) )->text() );
110  $this->parser->addTrackingCategory( 'duplicate-args-category' );
111  }
112  $numberedArgs[$index] = $bits['value'];
113  unset( $namedArgs[$index] );
114  } else {
115  // Named parameter
116  $name = trim( $this->expand( $bits['name'], PPFrame::STRIP_COMMENTS ) );
117  if ( isset( $namedArgs[$name] ) || isset( $numberedArgs[$name] ) ) {
118  $this->parser->getOutput()->addWarning( wfMessage( 'duplicate-args-warning',
119  wfEscapeWikiText( $this->title ),
121  wfEscapeWikiText( $name ) )->text() );
122  $this->parser->addTrackingCategory( 'duplicate-args-category' );
123  }
124  $namedArgs[$name] = $bits['value'];
125  unset( $numberedArgs[$name] );
126  }
127  }
128  }
129  return new PPTemplateFrame_Hash( $this->preprocessor, $this, $numberedArgs, $namedArgs, $title );
130  }
131 
139  public function cachedExpand( $key, $root, $flags = 0 ) {
140  // we don't have a parent, so we don't have a cache
141  return $this->expand( $root, $flags );
142  }
143 
150  public function expand( $root, $flags = 0 ) {
151  static $expansionDepth = 0;
152  if ( is_string( $root ) ) {
153  return $root;
154  }
155 
156  if ( ++$this->parser->mPPNodeCount > $this->parser->mOptions->getMaxPPNodeCount() ) {
157  $this->parser->limitationWarn( 'node-count-exceeded',
158  $this->parser->mPPNodeCount,
159  $this->parser->mOptions->getMaxPPNodeCount()
160  );
161  return '<span class="error">Node-count limit exceeded</span>';
162  }
163  if ( $expansionDepth > $this->parser->mOptions->getMaxPPExpandDepth() ) {
164  $this->parser->limitationWarn( 'expansion-depth-exceeded',
165  $expansionDepth,
166  $this->parser->mOptions->getMaxPPExpandDepth()
167  );
168  return '<span class="error">Expansion depth limit exceeded</span>';
169  }
170  ++$expansionDepth;
171  if ( $expansionDepth > $this->parser->mHighestExpansionDepth ) {
172  $this->parser->mHighestExpansionDepth = $expansionDepth;
173  }
174 
175  $outStack = [ '', '' ];
176  $iteratorStack = [ false, $root ];
177  $indexStack = [ 0, 0 ];
178 
179  while ( count( $iteratorStack ) > 1 ) {
180  $level = count( $outStack ) - 1;
181  $iteratorNode =& $iteratorStack[$level];
182  $out =& $outStack[$level];
183  $index =& $indexStack[$level];
184 
185  if ( is_array( $iteratorNode ) ) {
186  if ( $index >= count( $iteratorNode ) ) {
187  // All done with this iterator
188  $iteratorStack[$level] = false;
189  $contextNode = false;
190  } else {
191  $contextNode = $iteratorNode[$index];
192  $index++;
193  }
194  } elseif ( $iteratorNode instanceof PPNode_Hash_Array ) {
195  if ( $index >= $iteratorNode->getLength() ) {
196  // All done with this iterator
197  $iteratorStack[$level] = false;
198  $contextNode = false;
199  } else {
200  $contextNode = $iteratorNode->item( $index );
201  $index++;
202  }
203  } else {
204  // Copy to $contextNode and then delete from iterator stack,
205  // because this is not an iterator but we do have to execute it once
206  $contextNode = $iteratorStack[$level];
207  $iteratorStack[$level] = false;
208  }
209 
210  $newIterator = false;
211  $contextName = false;
212  $contextChildren = false;
213 
214  if ( $contextNode === false ) {
215  // nothing to do
216  } elseif ( is_string( $contextNode ) ) {
217  $out .= $contextNode;
218  } elseif ( $contextNode instanceof PPNode_Hash_Array ) {
219  $newIterator = $contextNode;
220  } elseif ( $contextNode instanceof PPNode_Hash_Attr ) {
221  // No output
222  } elseif ( $contextNode instanceof PPNode_Hash_Text ) {
223  $out .= $contextNode->value;
224  } elseif ( $contextNode instanceof PPNode_Hash_Tree ) {
225  $contextName = $contextNode->name;
226  $contextChildren = $contextNode->getRawChildren();
227  } elseif ( is_array( $contextNode ) ) {
228  // Node descriptor array
229  if ( count( $contextNode ) !== 2 ) {
230  throw new MWException( __METHOD__ .
231  ': found an array where a node descriptor should be' );
232  }
233  list( $contextName, $contextChildren ) = $contextNode;
234  } else {
235  throw new MWException( __METHOD__ . ': Invalid parameter type' );
236  }
237 
238  // Handle node descriptor array or tree object
239  if ( $contextName === false ) {
240  // Not a node, already handled above
241  } elseif ( $contextName[0] === '@' ) {
242  // Attribute: no output
243  } elseif ( $contextName === 'template' ) {
244  # Double-brace expansion
245  $bits = PPNode_Hash_Tree::splitRawTemplate( $contextChildren );
246  if ( $flags & PPFrame::NO_TEMPLATES ) {
247  $newIterator = $this->virtualBracketedImplode(
248  '{{', '|', '}}',
249  $bits['title'],
250  $bits['parts']
251  );
252  } else {
253  $ret = $this->parser->braceSubstitution( $bits, $this );
254  if ( isset( $ret['object'] ) ) {
255  $newIterator = $ret['object'];
256  } else {
257  $out .= $ret['text'];
258  }
259  }
260  } elseif ( $contextName === 'tplarg' ) {
261  # Triple-brace expansion
262  $bits = PPNode_Hash_Tree::splitRawTemplate( $contextChildren );
263  if ( $flags & PPFrame::NO_ARGS ) {
264  $newIterator = $this->virtualBracketedImplode(
265  '{{{', '|', '}}}',
266  $bits['title'],
267  $bits['parts']
268  );
269  } else {
270  $ret = $this->parser->argSubstitution( $bits, $this );
271  if ( isset( $ret['object'] ) ) {
272  $newIterator = $ret['object'];
273  } else {
274  $out .= $ret['text'];
275  }
276  }
277  } elseif ( $contextName === 'comment' ) {
278  # HTML-style comment
279  # Remove it in HTML, pre+remove and STRIP_COMMENTS modes
280  # Not in RECOVER_COMMENTS mode (msgnw) though.
281  if ( ( $this->parser->ot['html']
282  || ( $this->parser->ot['pre'] && $this->parser->mOptions->getRemoveComments() )
283  || ( $flags & PPFrame::STRIP_COMMENTS )
284  ) && !( $flags & PPFrame::RECOVER_COMMENTS )
285  ) {
286  $out .= '';
287  } elseif ( $this->parser->ot['wiki'] && !( $flags & PPFrame::RECOVER_COMMENTS ) ) {
288  # Add a strip marker in PST mode so that pstPass2() can
289  # run some old-fashioned regexes on the result.
290  # Not in RECOVER_COMMENTS mode (extractSections) though.
291  $out .= $this->parser->insertStripItem( $contextChildren[0] );
292  } else {
293  # Recover the literal comment in RECOVER_COMMENTS and pre+no-remove
294  $out .= $contextChildren[0];
295  }
296  } elseif ( $contextName === 'ignore' ) {
297  # Output suppression used by <includeonly> etc.
298  # OT_WIKI will only respect <ignore> in substed templates.
299  # The other output types respect it unless NO_IGNORE is set.
300  # extractSections() sets NO_IGNORE and so never respects it.
301  if ( ( !isset( $this->parent ) && $this->parser->ot['wiki'] )
302  || ( $flags & PPFrame::NO_IGNORE )
303  ) {
304  $out .= $contextChildren[0];
305  } else {
306  // $out .= '';
307  }
308  } elseif ( $contextName === 'ext' ) {
309  # Extension tag
310  $bits = PPNode_Hash_Tree::splitRawExt( $contextChildren ) +
311  [ 'attr' => null, 'inner' => null, 'close' => null ];
312  if ( $flags & PPFrame::NO_TAGS ) {
313  $s = '<' . $bits['name']->getFirstChild()->value;
314  // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
315  if ( $bits['attr'] ) {
316  $s .= $bits['attr']->getFirstChild()->value;
317  }
318  // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
319  if ( $bits['inner'] ) {
320  $s .= '>' . $bits['inner']->getFirstChild()->value;
321  // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
322  if ( $bits['close'] ) {
323  $s .= $bits['close']->getFirstChild()->value;
324  }
325  } else {
326  $s .= '/>';
327  }
328  $out .= $s;
329  } else {
330  $out .= $this->parser->extensionSubstitution( $bits, $this );
331  }
332  } elseif ( $contextName === 'h' ) {
333  # Heading
334  if ( $this->parser->ot['html'] ) {
335  # Expand immediately and insert heading index marker
336  $s = $this->expand( $contextChildren, $flags );
337  $bits = PPNode_Hash_Tree::splitRawHeading( $contextChildren );
338  $titleText = $this->title->getPrefixedDBkey();
339  $this->parser->mHeadings[] = [ $titleText, $bits['i'] ];
340  $serial = count( $this->parser->mHeadings ) - 1;
341  $marker = Parser::MARKER_PREFIX . "-h-$serial-" . Parser::MARKER_SUFFIX;
342  $s = substr( $s, 0, $bits['level'] ) . $marker . substr( $s, $bits['level'] );
343  $this->parser->mStripState->addGeneral( $marker, '' );
344  $out .= $s;
345  } else {
346  # Expand in virtual stack
347  $newIterator = $contextChildren;
348  }
349  } else {
350  # Generic recursive expansion
351  $newIterator = $contextChildren;
352  }
353 
354  if ( $newIterator !== false ) {
355  $outStack[] = '';
356  $iteratorStack[] = $newIterator;
357  $indexStack[] = 0;
358  } elseif ( $iteratorStack[$level] === false ) {
359  // Return accumulated value to parent
360  // With tail recursion
361  while ( $iteratorStack[$level] === false && $level > 0 ) {
362  $outStack[$level - 1] .= $out;
363  array_pop( $outStack );
364  array_pop( $iteratorStack );
365  array_pop( $indexStack );
366  $level--;
367  }
368  }
369  }
370  --$expansionDepth;
371  return $outStack[0];
372  }
373 
380  public function implodeWithFlags( $sep, $flags, ...$args ) {
381  $first = true;
382  $s = '';
383  foreach ( $args as $root ) {
384  if ( $root instanceof PPNode_Hash_Array ) {
385  $root = $root->value;
386  }
387  if ( !is_array( $root ) ) {
388  $root = [ $root ];
389  }
390  foreach ( $root as $node ) {
391  if ( $first ) {
392  $first = false;
393  } else {
394  $s .= $sep;
395  }
396  $s .= $this->expand( $node, $flags );
397  }
398  }
399  return $s;
400  }
401 
409  public function implode( $sep, ...$args ) {
410  $first = true;
411  $s = '';
412  foreach ( $args as $root ) {
413  if ( $root instanceof PPNode_Hash_Array ) {
414  $root = $root->value;
415  }
416  if ( !is_array( $root ) ) {
417  $root = [ $root ];
418  }
419  foreach ( $root as $node ) {
420  if ( $first ) {
421  $first = false;
422  } else {
423  $s .= $sep;
424  }
425  $s .= $this->expand( $node );
426  }
427  }
428  return $s;
429  }
430 
439  public function virtualImplode( $sep, ...$args ) {
440  $out = [];
441  $first = true;
442 
443  foreach ( $args as $root ) {
444  if ( $root instanceof PPNode_Hash_Array ) {
445  $root = $root->value;
446  }
447  if ( !is_array( $root ) ) {
448  $root = [ $root ];
449  }
450  foreach ( $root as $node ) {
451  if ( $first ) {
452  $first = false;
453  } else {
454  $out[] = $sep;
455  }
456  $out[] = $node;
457  }
458  }
459  return new PPNode_Hash_Array( $out );
460  }
461 
471  public function virtualBracketedImplode( $start, $sep, $end, ...$args ) {
472  $out = [ $start ];
473  $first = true;
474 
475  foreach ( $args as $root ) {
476  if ( $root instanceof PPNode_Hash_Array ) {
477  $root = $root->value;
478  }
479  if ( !is_array( $root ) ) {
480  $root = [ $root ];
481  }
482  foreach ( $root as $node ) {
483  if ( $first ) {
484  $first = false;
485  } else {
486  $out[] = $sep;
487  }
488  $out[] = $node;
489  }
490  }
491  $out[] = $end;
492  return new PPNode_Hash_Array( $out );
493  }
494 
495  public function __toString() {
496  return 'frame{}';
497  }
498 
503  public function getPDBK( $level = false ) {
504  if ( $level === false ) {
505  return $this->title->getPrefixedDBkey();
506  } else {
507  return $this->titleCache[$level] ?? false;
508  }
509  }
510 
514  public function getArguments() {
515  return [];
516  }
517 
521  public function getNumberedArguments() {
522  return [];
523  }
524 
528  public function getNamedArguments() {
529  return [];
530  }
531 
537  public function isEmpty() {
538  return true;
539  }
540 
545  public function getArgument( $name ) {
546  return false;
547  }
548 
556  public function loopCheck( $title ) {
557  return !isset( $this->loopCheckHash[$title->getPrefixedDBkey()] );
558  }
559 
565  public function isTemplate() {
566  return false;
567  }
568 
574  public function getTitle() {
575  return $this->title;
576  }
577 
583  public function setVolatile( $flag = true ) {
584  $this->volatile = $flag;
585  }
586 
592  public function isVolatile() {
593  return $this->volatile;
594  }
595 
601  public function setTTL( $ttl ) {
602  if ( $ttl !== null && ( $this->ttl === null || $ttl < $this->ttl ) ) {
603  $this->ttl = $ttl;
604  }
605  }
606 
612  public function getTTL() {
613  return $this->ttl;
614  }
615 }
PPFrame_Hash\getArguments
getArguments()
Definition: PPFrame_Hash.php:514
PPFrame\STRIP_COMMENTS
const STRIP_COMMENTS
Definition: PPFrame.php:31
PPFrame_Hash\$depth
$depth
Recursion depth of this frame, top = 0 Note that this is NOT the same as expansion depth in expand()
Definition: PPFrame_Hash.php:55
PPFrame_Hash\$childExpansionCache
array $childExpansionCache
Definition: PPFrame_Hash.php:63
PPFrame_Hash\getPDBK
getPDBK( $level=false)
Definition: PPFrame_Hash.php:503
PPFrame_Hash\getNumberedArguments
getNumberedArguments()
Definition: PPFrame_Hash.php:521
PPFrame_Hash\expand
expand( $root, $flags=0)
Definition: PPFrame_Hash.php:150
PPFrame\NO_ARGS
const NO_ARGS
Definition: PPFrame.php:29
PPNode_Hash_Tree\splitRawExt
static splitRawExt(array $children)
Like splitExt() but for a raw child array.
Definition: PPNode_Hash_Tree.php:266
Title\getPrefixedDBkey
getPrefixedDBkey()
Get the prefixed database key form.
Definition: Title.php:1838
PPFrame_Hash
An expansion frame, used as a context to expand the result of preprocessToObj()
Definition: PPFrame_Hash.php:27
PPFrame\NO_IGNORE
const NO_IGNORE
Definition: PPFrame.php:32
PPFrame_Hash\getNamedArguments
getNamedArguments()
Definition: PPFrame_Hash.php:528
PPFrame_Hash\getTTL
getTTL()
Get the TTL.
Definition: PPFrame_Hash.php:612
PPFrame_Hash\__construct
__construct( $preprocessor)
Definition: PPFrame_Hash.php:68
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1219
$s
$s
Definition: mergeMessageFileList.php:185
PPNode_Hash_Tree\splitRawHeading
static splitRawHeading(array $children)
Like splitHeading() but for a raw child array.
Definition: PPNode_Hash_Tree.php:311
PPFrame_Hash\$loopCheckHash
$loopCheckHash
Hashtable listing templates which are disallowed for expansion in this frame, having been encountered...
Definition: PPFrame_Hash.php:49
Parser\MARKER_PREFIX
const MARKER_PREFIX
Definition: Parser.php:145
PPFrame_Hash\implode
implode( $sep,... $args)
Implode with no flags specified This previously called implodeWithFlags but has now been inlined to r...
Definition: PPFrame_Hash.php:409
PPFrame\RECOVER_COMMENTS
const RECOVER_COMMENTS
Definition: PPFrame.php:33
PPFrame\NO_TEMPLATES
const NO_TEMPLATES
Definition: PPFrame.php:30
Preprocessor
Definition: Preprocessor.php:30
PPFrame_Hash\$titleCache
$titleCache
Definition: PPFrame_Hash.php:43
PPFrame_Hash\setVolatile
setVolatile( $flag=true)
Set the volatile flag.
Definition: PPFrame_Hash.php:583
MWException
MediaWiki exception.
Definition: MWException.php:29
PPFrame_Hash\__toString
__toString()
Definition: PPFrame_Hash.php:495
PPFrame_Hash\setTTL
setTTL( $ttl)
Set the TTL.
Definition: PPFrame_Hash.php:601
$args
if( $line===false) $args
Definition: mcc.php:124
PPFrame_Hash\isEmpty
isEmpty()
Returns true if there are no arguments in this frame.
Definition: PPFrame_Hash.php:537
PPFrame_Hash\getArgument
getArgument( $name)
Definition: PPFrame_Hash.php:545
PPNode_Hash_Tree\splitRawTemplate
static splitRawTemplate(array $children)
Like splitTemplate() but for a raw child array.
Definition: PPNode_Hash_Tree.php:344
PPFrame_Hash\virtualBracketedImplode
virtualBracketedImplode( $start, $sep, $end,... $args)
Virtual implode with brackets.
Definition: PPFrame_Hash.php:471
PPFrame_Hash\virtualImplode
virtualImplode( $sep,... $args)
Makes an object that, when expand()ed, will be the same as one obtained with implode()
Definition: PPFrame_Hash.php:439
PPNode_Hash_Array
Definition: PPNode_Hash_Array.php:26
PPTemplateFrame_Hash
Expansion frame with template arguments.
Definition: PPTemplateFrame_Hash.php:27
PPFrame_Hash\getTitle
getTitle()
Get a title of frame.
Definition: PPFrame_Hash.php:574
PPFrame_Hash\$preprocessor
Preprocessor $preprocessor
Definition: PPFrame_Hash.php:37
PPFrame\NO_TAGS
const NO_TAGS
Definition: PPFrame.php:34
PPFrame
Definition: PPFrame.php:28
PPFrame_Hash\$volatile
$volatile
Definition: PPFrame_Hash.php:57
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:1487
Parser
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition: Parser.php:84
PPNode_Hash_Attr
Definition: PPNode_Hash_Attr.php:26
PPFrame_Hash\newChild
newChild( $args=false, $title=false, $indexOffset=0)
Create a new child frame $args is optionally a multi-root PPNode or array containing the template arg...
Definition: PPFrame_Hash.php:88
Title
Represents a title within MediaWiki.
Definition: Title.php:42
PPNode_Hash_Text
Definition: PPNode_Hash_Text.php:26
PPFrame_Hash\isVolatile
isVolatile()
Get the volatile flag.
Definition: PPFrame_Hash.php:592
PPFrame_Hash\implodeWithFlags
implodeWithFlags( $sep, $flags,... $args)
Definition: PPFrame_Hash.php:380
PPFrame_Hash\$parser
Parser $parser
Definition: PPFrame_Hash.php:32
PPFrame_Hash\$ttl
$ttl
Definition: PPFrame_Hash.php:58
PPFrame_Hash\loopCheck
loopCheck( $title)
Returns true if the infinite loop check is OK, false if a loop is detected.
Definition: PPFrame_Hash.php:556
PPFrame_Hash\isTemplate
isTemplate()
Return true if the frame is a template frame.
Definition: PPFrame_Hash.php:565
PPNode_Hash_Tree
Definition: PPNode_Hash_Tree.php:26
PPFrame_Hash\$title
Title $title
Definition: PPFrame_Hash.php:42
PPFrame_Hash\cachedExpand
cachedExpand( $key, $root, $flags=0)
Definition: PPFrame_Hash.php:139