Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4110071
Frame.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
5 KB
Referenced Files
None
Subscribers
None
Frame.php
View Options
<?php
declare
(
strict_types
=
1
);
namespace
Wikimedia\Parsoid\Wt2Html
;
use
Wikimedia\Parsoid\Config\Env
;
use
Wikimedia\Parsoid\Tokens\EOFTk
;
use
Wikimedia\Parsoid\Tokens\KV
;
use
Wikimedia\Parsoid\Tokens\SourceRange
;
use
Wikimedia\Parsoid\Tokens\Token
;
use
Wikimedia\Parsoid\Utils\PHPUtils
;
use
Wikimedia\Parsoid\Utils\PipelineUtils
;
use
Wikimedia\Parsoid\Utils\Title
;
use
Wikimedia\Parsoid\Utils\TokenUtils
;
/**
* A frame represents a template expansion scope including parameters passed
* to the template (args). It provides a generic 'expand' method which
* expands / converts individual parameter values in its scope. It also
* provides methods to check if another expansion would lead to loops or
* exceed the maximum expansion depth.
*/
class
Frame
{
/** @var ?Frame */
private
$parentFrame
;
/** @var Env */
private
$env
;
/** @var Title */
private
$title
;
/** @var Params */
private
$args
;
/** @var string */
private
$srcText
;
/** @var int */
private
$depth
;
/**
* @param Title $title
* @param Env $env
* @param KV[] $args
* @param string $srcText
* @param ?Frame $parentFrame
*/
public
function
__construct
(
Title
$title
,
Env
$env
,
array
$args
,
string
$srcText
,
?
Frame
$parentFrame
=
null
)
{
$this
->
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
;
}
}
public
function
getEnv
():
Env
{
return
$this
->
env
;
}
public
function
getTitle
():
Title
{
return
$this
->
title
;
}
public
function
getArgs
():
Params
{
return
$this
->
args
;
}
public
function
getSrcText
():
string
{
return
$this
->
srcText
;
}
/**
* Create a new child frame.
* @param Title $title
* @param KV[] $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 array<Token|string> $chunk
* @param array $options
* @return array<Token|string>
*/
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'
=>
'peg-tokens-to-expanded-tokens'
,
'pipelineOpts'
=>
[
'isInclude'
=>
$this
->
depth
>
0
,
'expandTemplates'
=>
$options
[
'expandTemplates'
],
'inTemplate'
=>
$options
[
'inTemplate'
],
'attrExpansion'
=>
$options
[
'attrExpansion'
]
??
false
],
'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
;
}
/**
* @param mixed $arg
* @param SourceRange $srcOffsets
* @return array
*/
private
function
expandArg
(
$arg
,
SourceRange
$srcOffsets
):
array
{
if
(
is_string
(
$arg
)
)
{
return
[
$arg
];
}
else
{
return
$this
->
expand
(
$arg
,
[
'expandTemplates'
=>
true
,
'inTemplate'
=>
true
,
'srcOffsets'
=>
$srcOffsets
,
]
);
}
}
/**
* @param Token $tplArgToken
* @return array tokens representing the arg value
*/
public
function
expandTemplateArg
(
Token
$tplArgToken
):
array
{
$args
=
$this
->
args
->
named
();
$attribs
=
$tplArgToken
->
attribs
;
$expandedKeyToks
=
$this
->
expandArg
(
$attribs
[
0
]->
k
,
$attribs
[
0
]->
srcOffsets
->
key
);
$argName
=
trim
(
TokenUtils
::
tokensToString
(
$expandedKeyToks
)
);
$res
=
$args
[
'dict'
][
$argName
]
??
null
;
if
(
$res
!==
null
)
{
$res
=
isset
(
$args
[
'namedArgs'
][
$argName
]
)
?
TokenUtils
::
tokenTrim
(
$res
)
:
$res
;
return
is_string
(
$res
)
?
[
$res
]
:
$res
;
}
elseif
(
count
(
$attribs
)
>
1
)
{
return
$this
->
expandArg
(
$attribs
[
1
]->
v
,
$attribs
[
1
]->
srcOffsets
->
value
);
}
else
{
return
array_merge
(
[
'{{{'
],
$expandedKeyToks
,
[
'}}}'
]
);
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Tue, Aug 18, 21:29 (5 d, 43 m ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
89/80/31e0ab03a4dfb2d4e88596fdf19a
Default Alt Text
Frame.php (5 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment