Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4119518
SectionMetadata.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
14 KB
Referenced Files
None
Subscribers
None
SectionMetadata.php
View Options
<?php
declare
(
strict_types
=
1
);
namespace
Wikimedia\Parsoid\Core
;
use
Wikimedia\JsonCodec\JsonCodecable
;
use
Wikimedia\JsonCodec\JsonCodecableTrait
;
use
Wikimedia\Parsoid\Utils\CompatJsonCodec
;
/**
* Section metadata for generating TOC.
*
* This is not the complete data for the article section, just the
* information needed to generate the table of contents.
*
* For now, this schema matches whatever is generated by Parser.php.
* Parsoid will attempt to match this output for now.
*
* Parser.php::finalizeHeadings() is the authoritative source for how
* some of these properties are computed right now, especially for the
* $line, $anchor, and $linkAnchor properties below.
*
* Linker.php::tocLine() and ::makeHeadline() demonstrate how these
* properties are used to create headings and table of contents lines.
*/
class
SectionMetadata
implements
\JsonSerializable
,
JsonCodecable
{
use
JsonCodecableTrait
;
/**
* The heading tag level: a 1 here means an <H1> tag was used, a
* 2 means an <H2> tag was used, etc.
*/
public
int
$hLevel
;
/**
* This is a one-indexed TOC level and the nesting level.
* So, if a page has a H2-H4-H6, then, those levels 2,4,6
* correspond to TOC-levels 1,2,3.
*/
public
int
$tocLevel
;
/**
* HTML heading of the section. Only a narrow set of HTML tags are allowed here.
*
* This starts with the parsed headline seen in wikitext and
* - replaces links with link text
* - processes extension strip markers
* - removes style, script tags
* - strips all HTML tags except the following tags (from Parser.php)
* . <sup> and <sub> (T10393)
* . <i> (T28375)
* . <b> (r105284)
* . <bdi> (T74884)
* . <span dir="rtl"> and <span dir="ltr"> (T37167)
* . <s> and <strike> (T35715)
* . <q> (T251672)
* We strip any parameter from accepted tags, except dir="rtl|ltr" from <span>,
* to allow setting directionality in toc items.
*
* @note This should be converted into the proper html variant.
*/
public
string
$line
;
/**
* TOC number string (3.1.3, 4.5.2, etc.)
*
* @note This should be localized into the parser target language.
*/
public
string
$number
;
/**
* Section id (integer, assigned in depth first traversal order)
* Template generated sections get a "T-" prefix.
*/
public
string
$index
;
/**
* The title of the page that generated this heading.
* For template-generated sections, this will be the template title.
* This string is in "prefixed DB key" format.
*/
public
?
string
$fromTitle
;
/**
* Codepoint offset where the section shows up in wikitext; this is null
* if this section comes from a template, if it comes from a literal
* HTML <h_> tag, or otherwise doesn't correspond to a "preprocessor
* section".
* @note This is measured in codepoints, not bytes; you should use
* appropriate multi-byte aware string functions, *not* substr().
* Similarly, in JavaScript, be careful not to confuse JavaScript
* UCS-2 "characters" with codepoints.
*/
public
?
int
$codepointOffset
;
/**
* Anchor attribute.
*
* This property is the "true" value of the ID attribute, and should be
* used when looking up a heading or setting an attribute, for example
* using Document.getElementById() or Element.setAttribute('id',...).
*
* This value is *not* HTML-entity escaped; if you are writing HTML
* as a literal string, you should still entity-escape ampersands and
* single/double quotes as appropriate.
*
* This value is *not* URL-escaped either; instead use the `linkAnchor`
* property if you are constructing a URL to target this section.
*
* The anchor attribute is based on the $line property, but does extra
* processing to turn it into a valid attribute:
* - strip all HTML tags,
* - normalizes section name
* - normalizes section name whitespace
* - decodes char references
* - makes it a valid HTML id attribute value
* (HTML5 / HTML4 based on $wgFragmentMode property)
* - dedupes (case-insensitively) identical anchors by adding "_$n" suffixes
*/
public
string
$anchor
;
/**
* Anchor URL fragment.
*
* This is very similar to the $anchor property, but is appropriately
* URL-escaped to make it appropriate to use in constructing a URL
* fragment link. You should almost always prepend a `#` symbol
* to `linkAnchor` if you are using it correctly. You are still
* responsible for HTML-escaping the resulting URL if you are emitting
* this as an HTML attribute.
*/
public
string
$linkAnchor
;
/**
* Arbitrary data attached to this section by extensions. This
* data will be stored and cached in the ParserOutput object along
* with the rest of the section data, and made available to external
* clients via the action API.
*
* This method is provided to overcome the unsafe practice of attaching
* extra information to a section by directly assigning member variables.
*
* See ParserOutput::setExtensionData() for more information on typical
* use.
*/
private
array
$extensionData
;
/**
* @param int $tocLevel One-indexed TOC level and the nesting level
* @param int $hLevel The heading tag level
* @param string $line Stripped headline text
* @param string $number TOC number string (3.1.3, 4.5.2, etc)
* @param string $index Section id
* @param ?string $fromTitle The title of the page or template that
* generated this heading, or null.
* @param ?int $codepointOffset Codepoint offset (# of characters) where the
* section shows up in wikitext, or null if this doesn't correspond to
* a "preprocesor section". (Be careful if using JavaScript, as
* JavaScript "characters" are UCS-2 encoded and don't correspond
* directly to code points.)
* @param string $anchor "True" value of the ID attribute
* @param string $linkAnchor URL-escaped value of the anchor, for use in
* constructing a URL fragment link
* @param ?array $extensionData Extension data passed in as an associative array
*/
public
function
__construct
(
// This is a great candidate for named arguments in PHP 8.0+
int
$tocLevel
=
0
,
int
$hLevel
=
-
1
,
string
$line
=
''
,
string
$number
=
''
,
string
$index
=
''
,
?
string
$fromTitle
=
null
,
?
int
$codepointOffset
=
null
,
string
$anchor
=
''
,
string
$linkAnchor
=
''
,
?
array
$extensionData
=
null
)
{
$this
->
tocLevel
=
$tocLevel
;
$this
->
line
=
$line
;
$this
->
hLevel
=
$hLevel
;
$this
->
number
=
$number
;
$this
->
index
=
$index
;
$this
->
fromTitle
=
$fromTitle
;
$this
->
codepointOffset
=
$codepointOffset
;
$this
->
anchor
=
$anchor
;
$this
->
linkAnchor
=
$linkAnchor
;
$this
->
extensionData
=
$extensionData
??
[];
}
/**
* Attaches arbitrary data to this SectionMetadata object. This
* can be used to store some information about this section in the
* ParserOutput object for later use during page output. The data
* will be cached along with the ParserOutput object.
*
* This method is provided to overcome the unsafe practice of
* attaching extra information to a section by directly assigning
* member variables.
*
* See ParserOutput::setExtensionData() in core for further information
* about typical usage in hooks.
*
* Setting conflicting values for the same key is not allowed.
* If you call ::setExtensionData() multiple times with the same key
* on a SectionMetadata, is is expected that the value will be identical
* each time. If you want to collect multiple pieces of data under a
* single key, use ::appendExtensionData().
*
* @note Only scalar values (numbers, strings, or arrays) are
* supported as a value. (A future revision will allow anything
* that core's JsonCodec can handle.) Attempts to set other types
* as extension data values will break ParserCache for the page.
*
* @todo When more complex values than scalar values are supported,
* TOCData::__clone should be updated to take that into account.
*
* @param string $key The key for accessing the data. Extensions
* should take care to avoid conflicts in naming keys. It is
* suggested to use the extension's name as a prefix. Using
* the prefix `mw:` is reserved for core.
*
* @param mixed $value The value to set.
* Setting a value to null is equivalent to removing the value.
*/
public
function
setExtensionData
(
string
$key
,
$value
):
void
{
if
(
array_key_exists
(
$key
,
$this
->
extensionData
)
&&
$this
->
extensionData
[
$key
]
!==
$value
)
{
throw
new
\InvalidArgumentException
(
"Conflicting data for $key"
);
}
if
(
$value
===
null
)
{
unset
(
$this
->
extensionData
[
$key
]
);
}
else
{
$this
->
extensionData
[
$key
]
=
$value
;
}
}
/**
* Appends arbitrary data to this SectionMetadata. This can be used
* to store some information about the section in the ParserOutput object for later
* use during page output.
*
* See ::setExtensionData() for more details on rationale and use.
*
* @param string $key The key for accessing the data. Extensions should take care to avoid
* conflicts in naming keys. It is suggested to use the extension's name as a prefix.
*
* @param int|string $value The value to append to the list.
* @return never This method is not yet implemented.
*/
public
function
appendExtensionData
(
string
$key
,
$value
):
void
{
// This implementation would mirror that of
// ParserOutput::appendExtensionData, but let's defer implementing
// this until we're sure we need it. In particular, we might need
// to figure out how a merge on section data is expected to work
// before we can determine the right semantics for this.
throw
new
\InvalidArgumentException
(
"Not yet implemented"
);
}
/**
* Gets extension data previously attached to this SectionMetadata.
*
* @param string $key The key to look up
* @return mixed|null The value(s) previously set for the given key using
* ::setExtensionData() or ::appendExtensionData(), or null if no
* value was set for this key.
*/
public
function
getExtensionData
(
$key
)
{
$value
=
$this
->
extensionData
[
$key
]
??
null
;
return
$value
;
}
/**
* Alias for :toLegacy(), for b/c compatibility only.
* @deprecated
* @return array
*/
public
function
toArray
():
array
{
return
$this
->
toLegacy
();
}
/**
* Alias for :fromLegacy(), for b/c compatibility only.
* @deprecated
* @param array $data
* @return SectionMetadata
*/
public
static
function
fromArray
(
array
$data
):
SectionMetadata
{
return
self
::
fromLegacy
(
$data
);
}
/**
* Create a new SectionMetadata object from an array in the legacy
* format returned by the action API.
*
* This is useful for backward-compatibility, but is expected to
* be replaced by conversion to/from JSON in the future.
*
* @param array $data Associative array with section metadata
* @return SectionMetadata
*/
public
static
function
fromLegacy
(
array
$data
):
SectionMetadata
{
return
new
SectionMetadata
(
$data
[
'toclevel'
]
??
0
,
(
int
)(
$data
[
'level'
]
??
-
1
),
$data
[
'line'
]
??
''
,
$data
[
'number'
]
??
''
,
$data
[
'index'
]
??
''
,
(
$data
[
'fromtitle'
]
??
false
)
?:
null
,
$data
[
'byteoffset'
]
??
null
,
// T319141: actually "codepoint offset"
$data
[
'anchor'
]
??
''
,
$data
[
'linkAnchor'
]
??
$data
[
'anchor'
]
??
''
,
$data
[
'extensionData'
]
??
null
);
}
/**
* Return as associative array, in the format returned by the
* action API (including the order of fields and the value types).
*
* This is helpful as b/c support while we transition to objects.
* @return array
*/
public
function
toLegacy
():
array
{
$ret
=
[
'toclevel'
=>
$this
->
tocLevel
,
// cast $level to string in order to keep b/c for the parse api
'level'
=>
(
string
)
$this
->
hLevel
,
'line'
=>
$this
->
line
,
'number'
=>
$this
->
number
,
'index'
=>
$this
->
index
,
'fromtitle'
=>
$this
->
fromTitle
??
false
,
// T319141: legacy 'byteoffset' is actually "codepoint offset"
'byteoffset'
=>
$this
->
codepointOffset
,
'anchor'
=>
$this
->
anchor
,
'linkAnchor'
=>
$this
->
linkAnchor
,
];
// Micro-opt: Output 'extensionData' conditionally to avoid bloat
if
(
$this
->
extensionData
)
{
$ret
[
'extensionData'
]
=
$this
->
extensionData
;
}
return
$ret
;
}
/**
* @inheritDoc
*/
public
function
jsonSerialize
():
array
{
return
$this
->
toLegacy
();
}
// JsonCodecable interface
/** @inheritDoc */
public
function
toJsonArray
():
array
{
$ret
=
[];
if
(
$this
->
tocLevel
!==
0
)
{
$ret
[
'tocLevel'
]
=
$this
->
tocLevel
;
}
if
(
$this
->
hLevel
!==
-
1
)
{
$ret
[
'hLevel'
]
=
$this
->
hLevel
;
}
if
(
$this
->
line
!==
''
)
{
$ret
[
'line'
]
=
$this
->
line
;
}
if
(
$this
->
number
!==
''
)
{
$ret
[
'number'
]
=
$this
->
number
;
}
if
(
$this
->
index
!==
''
)
{
$ret
[
'index'
]
=
$this
->
index
;
}
if
(
$this
->
fromTitle
!==
null
)
{
$ret
[
'fromTitle'
]
=
$this
->
fromTitle
;
}
if
(
$this
->
codepointOffset
!==
null
)
{
$ret
[
'codepointOffset'
]
=
$this
->
codepointOffset
;
}
if
(
$this
->
anchor
!==
''
)
{
$ret
[
'anchor'
]
=
$this
->
anchor
;
}
if
(
$this
->
linkAnchor
!==
$this
->
anchor
)
{
$ret
[
'linkAnchor'
]
=
$this
->
linkAnchor
;
}
if
(
$this
->
extensionData
)
{
$ret
[
'extensionData'
]
=
$this
->
extensionData
;
}
return
$ret
;
}
/** @inheritDoc */
public
static
function
newFromJsonArray
(
array
$json
)
{
return
new
SectionMetadata
(
$json
[
'tocLevel'
]
??
0
,
$json
[
'hLevel'
]
??
-
1
,
$json
[
'line'
]
??
''
,
$json
[
'number'
]
??
''
,
$json
[
'index'
]
??
''
,
$json
[
'fromTitle'
]
??
null
,
$json
[
'codepointOffset'
]
??
null
,
$json
[
'anchor'
]
??
''
,
$json
[
'linkAnchor'
]
??
$json
[
'anchor'
]
??
''
,
$json
[
'extensionData'
]
??
null
);
}
// Pretty-printing
/**
* For use in parser tests and wherever else humans might appreciate
* some formatting in the JSON encoded output. For now, nothing special.
* @param int $indent Additional indentation to apply (defaults to zero)
* @return string
*/
public
function
prettyPrint
(
int
$indent
=
0
):
string
{
# Basic info
$buf
=
str_repeat
(
' '
,
$indent
+
$this
->
tocLevel
)
.
"h{$this->hLevel}"
;
$buf
.=
" index:{$this->index} toclevel:$this->tocLevel number:{$this->number}"
;
# Optional information
$title
=
$this
->
fromTitle
??
"NULL"
;
$offset
=
$this
->
codepointOffset
??
"NULL"
;
$buf
.=
" title:{$title} off:{$offset}"
;
# Anchors & link text
if
(
$this
->
anchor
===
$this
->
linkAnchor
)
{
$buf
.=
" anchor/linkAnchor:{$this->anchor}"
;
}
else
{
$buf
.=
" anchor:{$this->anchor} linkAnchor:{$this->linkAnchor}"
;
}
$line
=
$this
->
line
;
if
(
str_contains
(
$line
,
"
\n
"
)
)
{
// Handle cases where $line has "funny" characters
$line
=
json_encode
(
$line
);
}
$buf
.=
" line:{$line}"
;
# Extension data
if
(
$this
->
extensionData
)
{
$codec
=
new
CompatJsonCodec
();
$buf
.=
" ext:"
.
json_encode
(
$codec
->
toJsonArray
(
$this
->
extensionData
)
);
}
return
$buf
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Wed, Aug 19, 03:28 (1 w, 5 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
9f/34/1dd749fe8147521c5329f37725a3
Default Alt Text
SectionMetadata.php (14 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment