title = $title;
$this->env = $env;
$this->args = new Params( $args );
$this->srcText = $srcText;
if ( $parentFrame ) {
$this->parentFrame = $parentFrame;
$this->depth = $parentFrame->depth + 1;
} else {
$this->parentFrame = null;
$this->depth = 0;
}
}
/**
* @return Env
*/
public function getEnv(): Env {
return $this->env;
}
/**
* @return Title
*/
public function getTitle(): Title {
return $this->title;
}
/**
* @return Params
*/
public function getArgs(): Params {
return $this->args;
}
/**
* @return string
*/
public function getSrcText(): string {
return $this->srcText;
}
/**
* Create a new child frame.
* @param Title $title
* @param array $args
* @param string $srcText
* @return Frame
*/
public function newChild( Title $title, array $args, string $srcText ): Frame {
return new Frame( $title, $this->env, $args, $srcText, $this );
}
/**
* Expand / convert a thunk (a chunk of tokens not yet fully expanded).
* @param Token[] $chunk
* @param array $options
* @return Token[]
*/
public function expand( array $chunk, array $options ): array {
$this->env->log( 'debug', 'Frame.expand', $chunk );
if ( !$chunk ) {
return $chunk;
}
// Add an EOFTk if it isn't present
$content = $chunk;
if ( !( PHPUtils::lastItem( $chunk ) instanceof EOFTk ) ) {
$content[] = new EOFTk();
}
// Downstream template uses should be tracked and wrapped only if:
// - not in a nested template Ex: {{Templ:Foo}} and we are processing Foo
// - not in a template use context Ex: {{ .. | {{ here }} | .. }}
// - the attribute use is wrappable Ex: [[ ... | {{ .. link text }} ]]
$opts = [
'pipelineType' => 'tokens/x-mediawiki',
'pipelineOpts' => [
'isInclude' => $this->depth > 0,
'expandTemplates' => !empty( $options['expandTemplates'] ),
'inTemplate' => !empty( $options['inTemplate'] )
],
'sol' => true,
'srcOffsets' => $options['srcOffsets'] ?? null,
'tplArgs' => [ 'name' => null, 'title' => null, 'attribs' => [] ]
];
$tokens = PipelineUtils::processContentInPipeline( $this->env, $this, $content, $opts );
TokenUtils::stripEOFTkfromTokens( $tokens );
return $tokens;
}
/**
* Check if expanding a template would lead to a loop, or would exceed the
* maximum expansion depth.
*
* @param Title $title
* @param int $maxDepth
* @param bool $ignoreLoop
* @return ?string null => no error; non-null => error message
*/
public function loopAndDepthCheck( Title $title, int $maxDepth, bool $ignoreLoop ): ?string {
if ( $this->depth > $maxDepth ) {
// Too deep
return "Template recursion depth limit exceeded ($maxDepth): ";
}
if ( $ignoreLoop ) {
return null;
}
$frame = $this;
do {
if ( $title->equals( $frame->title ) ) {
// Loop detected
return 'Template loop detected: ';
}
$frame = $frame->parentFrame;
} while ( $frame );
// No loop detected.
return null;
}
}