wiki.techinc.nl/includes/parser/ParserOutput.php

903 lines
28 KiB
PHP
Raw Normal View History

<?php
/**
* Output of the PHP parser.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup Parser
*/
class ParserOutput extends CacheTime {
public $mText, # The output text
$mLanguageLinks, # List of the full text of language links, in the order they appear
$mCategories, # Map of category names to sort keys
Implement page status indicators Page status indicators are icons (or short text snippets) usually displayed in the top-right corner of the page, outside of the main content. Basically, <indicator name="foo">[[File:Foo.svg|20px]]</indicator> may be used on a page to place the icon in the indicator area. They are also known as top icons, page icons, heading icons or title icons. I found the discussion on bug 23796 highly illuminating. I suggest that everyone read it before suggesting different design choices. I spent some time with a thesaurus pondering the name. "Emblems" and "badges" were also considered, but the former has a much more limited meaning and the latter is already taken by Wikidata, with a similar but subtly different feature set. I am not aware of any naming conflicts ;) besides new talk page message "indicator" (used by core and Echo in some documents) and OOjs UI indicators (tiny icons like the arrow on a dropdown form element), which shouldn't be confusing. Potential use cases include: * "Lock" indicators for page protection levels * Featured/good article indicators * Redirect shortcuts display ("WP:VPT") * Links to help/manual for special pages * Coordinates?… or globe icon for inline pop-up maps Design features: * Skin-customizable. Skins can fully control where and how indicators are shown, or may just do <?php echo $this->getIndicators(); ?> to output the default structure. By default they are not shown at all. * Extension-customizable. Extensions can call ParserOutput::addIndicator() to insert an indicator from one of the numerous parser hooks. * Wiki-customizable. In addition to just using the parser functions, on-wiki styles and scripts can use the provided classes and ids (.mw-indicator, #mw-indicator-<name>) to customize their display. Design limitations: * Every indicator must have a unique identifier (name). It's not possible to create arrays, or to have several indicators with the same name. In case of duplicates, the latest occurrence of the parser function wins. * Indicators are displayed ordered by their names (and not occurrence order). This ensures consistency across pages and provides a simple means of ordering or grouping them. * Indicators are not stored, tracked or accessible outside of ParserOutput (in particular they're not in the page_props table). They are intended to merely reflect the content or metadata that is already present on the page, and not be data themselves. If you ever think you need to list pages with a given status indicator, instead figure out what it means and use the appropriate tracking category, special page report, already existing page_prop, or other means. Corresponding patch in Vector: I90a8ae15ac8275d084ea5f47b6b2684d5e6c7412. I'll implement support in the other three skins included in the tarball and document it on mediawiki.org after this is merged. Bug: 23796 Change-Id: I2389ff9a5332a2b1d033eb75f0946e5241cfaaf4
2014-09-24 10:44:16 +00:00
$mIndicators = array(), # Page status indicators, usually displayed in top-right corner
$mTitleText, # title text of the chosen language variant
$mLinks = array(), # 2-D map of NS/DBK to ID for the links in the document. ID=zero for broken.
$mTemplates = array(), # 2-D map of NS/DBK to ID for the template references. ID=zero for broken.
$mTemplateIds = array(), # 2-D map of NS/DBK to rev ID for the template references. ID=zero for broken.
$mImages = array(), # DB keys of the images used, in the array key only
$mFileSearchOptions = array(), # DB keys of the images used mapped to sha1 and MW timestamp
$mExternalLinks = array(), # External link URLs, in the key only
$mInterwikiLinks = array(), # 2-D map of prefix/DBK (in keys only) for the inline interwiki links in the document.
$mNewSection = false, # Show a new section link?
$mHideNewSection = false, # Hide the new section link?
$mNoGallery = false, # No gallery on category page? (__NOGALLERY__)
$mHeadItems = array(), # Items to put in the <head> section
$mModules = array(), # Modules to be loaded by the resource loader
$mModuleScripts = array(), # Modules of which only the JS will be loaded by the resource loader
$mModuleStyles = array(), # Modules of which only the CSSS will be loaded by the resource loader
$mModuleMessages = array(), # Modules of which only the messages will be loaded by the resource loader
$mJsConfigVars = array(), # JavaScript config variable for mw.config combined with this page
$mOutputHooks = array(), # Hook tags as per $wgParserOutputHooks
$mWarnings = array(), # Warning text to be returned to the user. Wikitext formatted, in the key only
$mSections = array(), # Table of contents
$mEditSectionTokens = false, # prefix/suffix markers if edit sections were output as tokens
$mProperties = array(), # Name/value pairs to be cached in the DB
$mTOCHTML = '', # HTML of the TOC
$mTimestamp, # Timestamp of the revision
$mTOCEnabled = true; # Whether TOC should be shown, can't override __NOTOC__
private $mIndexPolicy = ''; # 'index' or 'noindex'? Any other value will result in no change.
private $mAccessedOptions = array(); # List of ParserOptions (stored in the keys)
private $mSecondaryDataUpdates = array(); # List of DataUpdate, used to save info from the page somewhere else.
private $mCustomDataUpdateCount = 0; # Number of custom updaters in $mSecondaryDataUpdates.
private $mExtensionData = array(); # extra data used by extensions
private $mLimitReportData = array(); # Parser limit report data
private $mParseStartTime = array(); # Timestamps for getTimeSinceStart()
private $mPreventClickjacking = false; # Whether to emit X-Frame-Options: DENY
const EDITSECTION_REGEX =
'#<(?:mw:)?editsection page="(.*?)" section="(.*?)"(?:/>|>(.*?)(</(?:mw:)?editsection>))#';
public function __construct( $text = '', $languageLinks = array(), $categoryLinks = array(),
$containsOldMagic = false, $titletext = ''
) {
$this->mText = $text;
$this->mLanguageLinks = $languageLinks;
$this->mCategories = $categoryLinks;
$this->mContainsOldMagic = $containsOldMagic;
$this->mTitleText = $titletext;
}
public function getText() {
$text = $this->mText;
if ( $this->mEditSectionTokens ) {
$text = preg_replace_callback(
ParserOutput::EDITSECTION_REGEX,
function ( $m ) {
global $wgOut, $wgLang;
$editsectionPage = Title::newFromText( htmlspecialchars_decode( $m[1] ) );
$editsectionSection = htmlspecialchars_decode( $m[2] );
$editsectionContent = isset( $m[4] ) ? $m[3] : null;
if ( !is_object( $editsectionPage ) ) {
throw new MWException( "Bad parser output text." );
}
$skin = $wgOut->getSkin();
return call_user_func_array(
array( $skin, 'doEditSectionLink' ),
array( $editsectionPage, $editsectionSection,
$editsectionContent, $wgLang->getCode() )
);
},
$text
);
} else {
$text = preg_replace( ParserOutput::EDITSECTION_REGEX, '', $text );
}
// If you have an old cached version of this class - sorry, you can't disable the TOC
if ( isset( $this->mTOCEnabled ) && $this->mTOCEnabled ) {
$text = str_replace( array( Parser::TOC_START, Parser::TOC_END ), '', $text );
} else {
$text = preg_replace(
'#' . preg_quote( Parser::TOC_START ) . '.*?' . preg_quote( Parser::TOC_END ) . '#s',
'',
$text
);
}
return $text;
}
public function &getLanguageLinks() {
return $this->mLanguageLinks;
}
public function getInterwikiLinks() {
return $this->mInterwikiLinks;
}
public function getCategoryLinks() {
return array_keys( $this->mCategories );
}
public function &getCategories() {
return $this->mCategories;
}
Implement page status indicators Page status indicators are icons (or short text snippets) usually displayed in the top-right corner of the page, outside of the main content. Basically, <indicator name="foo">[[File:Foo.svg|20px]]</indicator> may be used on a page to place the icon in the indicator area. They are also known as top icons, page icons, heading icons or title icons. I found the discussion on bug 23796 highly illuminating. I suggest that everyone read it before suggesting different design choices. I spent some time with a thesaurus pondering the name. "Emblems" and "badges" were also considered, but the former has a much more limited meaning and the latter is already taken by Wikidata, with a similar but subtly different feature set. I am not aware of any naming conflicts ;) besides new talk page message "indicator" (used by core and Echo in some documents) and OOjs UI indicators (tiny icons like the arrow on a dropdown form element), which shouldn't be confusing. Potential use cases include: * "Lock" indicators for page protection levels * Featured/good article indicators * Redirect shortcuts display ("WP:VPT") * Links to help/manual for special pages * Coordinates?… or globe icon for inline pop-up maps Design features: * Skin-customizable. Skins can fully control where and how indicators are shown, or may just do <?php echo $this->getIndicators(); ?> to output the default structure. By default they are not shown at all. * Extension-customizable. Extensions can call ParserOutput::addIndicator() to insert an indicator from one of the numerous parser hooks. * Wiki-customizable. In addition to just using the parser functions, on-wiki styles and scripts can use the provided classes and ids (.mw-indicator, #mw-indicator-<name>) to customize their display. Design limitations: * Every indicator must have a unique identifier (name). It's not possible to create arrays, or to have several indicators with the same name. In case of duplicates, the latest occurrence of the parser function wins. * Indicators are displayed ordered by their names (and not occurrence order). This ensures consistency across pages and provides a simple means of ordering or grouping them. * Indicators are not stored, tracked or accessible outside of ParserOutput (in particular they're not in the page_props table). They are intended to merely reflect the content or metadata that is already present on the page, and not be data themselves. If you ever think you need to list pages with a given status indicator, instead figure out what it means and use the appropriate tracking category, special page report, already existing page_prop, or other means. Corresponding patch in Vector: I90a8ae15ac8275d084ea5f47b6b2684d5e6c7412. I'll implement support in the other three skins included in the tarball and document it on mediawiki.org after this is merged. Bug: 23796 Change-Id: I2389ff9a5332a2b1d033eb75f0946e5241cfaaf4
2014-09-24 10:44:16 +00:00
/**
* @since 1.25
*/
public function getIndicators() {
return $this->mIndicators;
}
public function getTitleText() {
return $this->mTitleText;
}
public function getSections() {
return $this->mSections;
}
public function getEditSectionTokens() {
return $this->mEditSectionTokens;
}
public function &getLinks() {
return $this->mLinks;
}
public function &getTemplates() {
return $this->mTemplates;
}
public function &getTemplateIds() {
return $this->mTemplateIds;
}
public function &getImages() {
return $this->mImages;
}
public function &getFileSearchOptions() {
return $this->mFileSearchOptions;
}
public function &getExternalLinks() {
return $this->mExternalLinks;
}
public function getNoGallery() {
return $this->mNoGallery;
}
public function getHeadItems() {
return $this->mHeadItems;
}
public function getModules() {
return $this->mModules;
}
public function getModuleScripts() {
return $this->mModuleScripts;
}
public function getModuleStyles() {
return $this->mModuleStyles;
}
public function getModuleMessages() {
return $this->mModuleMessages;
}
/** @since 1.23 */
public function getJsConfigVars() {
return $this->mJsConfigVars;
}
public function getOutputHooks() {
return (array)$this->mOutputHooks;
}
public function getWarnings() {
return array_keys( $this->mWarnings );
}
public function getIndexPolicy() {
return $this->mIndexPolicy;
}
public function getTOCHTML() {
return $this->mTOCHTML;
}
public function getTimestamp() {
return $this->mTimestamp;
}
public function getLimitReportData() {
return $this->mLimitReportData;
}
public function getTOCEnabled() {
return $this->mTOCEnabled;
}
public function setText( $text ) {
return wfSetVar( $this->mText, $text );
}
public function setLanguageLinks( $ll ) {
return wfSetVar( $this->mLanguageLinks, $ll );
}
public function setCategoryLinks( $cl ) {
return wfSetVar( $this->mCategories, $cl );
}
public function setTitleText( $t ) {
return wfSetVar( $this->mTitleText, $t );
}
public function setSections( $toc ) {
return wfSetVar( $this->mSections, $toc );
}
public function setEditSectionTokens( $t ) {
return wfSetVar( $this->mEditSectionTokens, $t );
}
public function setIndexPolicy( $policy ) {
return wfSetVar( $this->mIndexPolicy, $policy );
}
public function setTOCHTML( $tochtml ) {
return wfSetVar( $this->mTOCHTML, $tochtml );
}
public function setTimestamp( $timestamp ) {
return wfSetVar( $this->mTimestamp, $timestamp );
}
public function setTOCEnabled( $flag ) {
return wfSetVar( $this->mTOCEnabled, $flag );
}
public function addCategory( $c, $sort ) {
$this->mCategories[$c] = $sort;
}
Implement page status indicators Page status indicators are icons (or short text snippets) usually displayed in the top-right corner of the page, outside of the main content. Basically, <indicator name="foo">[[File:Foo.svg|20px]]</indicator> may be used on a page to place the icon in the indicator area. They are also known as top icons, page icons, heading icons or title icons. I found the discussion on bug 23796 highly illuminating. I suggest that everyone read it before suggesting different design choices. I spent some time with a thesaurus pondering the name. "Emblems" and "badges" were also considered, but the former has a much more limited meaning and the latter is already taken by Wikidata, with a similar but subtly different feature set. I am not aware of any naming conflicts ;) besides new talk page message "indicator" (used by core and Echo in some documents) and OOjs UI indicators (tiny icons like the arrow on a dropdown form element), which shouldn't be confusing. Potential use cases include: * "Lock" indicators for page protection levels * Featured/good article indicators * Redirect shortcuts display ("WP:VPT") * Links to help/manual for special pages * Coordinates?… or globe icon for inline pop-up maps Design features: * Skin-customizable. Skins can fully control where and how indicators are shown, or may just do <?php echo $this->getIndicators(); ?> to output the default structure. By default they are not shown at all. * Extension-customizable. Extensions can call ParserOutput::addIndicator() to insert an indicator from one of the numerous parser hooks. * Wiki-customizable. In addition to just using the parser functions, on-wiki styles and scripts can use the provided classes and ids (.mw-indicator, #mw-indicator-<name>) to customize their display. Design limitations: * Every indicator must have a unique identifier (name). It's not possible to create arrays, or to have several indicators with the same name. In case of duplicates, the latest occurrence of the parser function wins. * Indicators are displayed ordered by their names (and not occurrence order). This ensures consistency across pages and provides a simple means of ordering or grouping them. * Indicators are not stored, tracked or accessible outside of ParserOutput (in particular they're not in the page_props table). They are intended to merely reflect the content or metadata that is already present on the page, and not be data themselves. If you ever think you need to list pages with a given status indicator, instead figure out what it means and use the appropriate tracking category, special page report, already existing page_prop, or other means. Corresponding patch in Vector: I90a8ae15ac8275d084ea5f47b6b2684d5e6c7412. I'll implement support in the other three skins included in the tarball and document it on mediawiki.org after this is merged. Bug: 23796 Change-Id: I2389ff9a5332a2b1d033eb75f0946e5241cfaaf4
2014-09-24 10:44:16 +00:00
/**
* @since 1.25
*/
public function setIndicator( $id, $content ) {
$this->mIndicators[$id] = $content;
}
public function addLanguageLink( $t ) {
$this->mLanguageLinks[] = $t;
}
public function addWarning( $s ) {
$this->mWarnings[$s] = 1;
}
public function addOutputHook( $hook, $data = false ) {
$this->mOutputHooks[] = array( $hook, $data );
}
public function setNewSection( $value ) {
$this->mNewSection = (bool)$value;
}
public function hideNewSection( $value ) {
$this->mHideNewSection = (bool)$value;
}
public function getHideNewSection() {
return (bool)$this->mHideNewSection;
}
public function getNewSection() {
return (bool)$this->mNewSection;
}
/**
* Checks, if a url is pointing to the own server
*
* @param string $internal The server to check against
* @param string $url The url to check
* @return bool
*/
public static function isLinkInternal( $internal, $url ) {
return (bool)preg_match( '/^' .
# If server is proto relative, check also for http/https links
( substr( $internal, 0, 2 ) === '//' ? '(?:https?:)?' : '' ) .
preg_quote( $internal, '/' ) .
# check for query/path/anchor or end of link in each case
'(?:[\?\/\#]|$)/i',
$url
);
}
public function addExternalLink( $url ) {
# We don't register links pointing to our own server, unless... :-)
global $wgServer, $wgRegisterInternalExternals;
$registerExternalLink = true;
if ( !$wgRegisterInternalExternals ) {
$registerExternalLink = !self::isLinkInternal( $wgServer, $url );
}
if ( $registerExternalLink ) {
$this->mExternalLinks[$url] = 1;
}
}
/**
* Record a local or interwiki inline link for saving in future link tables.
*
* @param Title $title
* @param int|null $id Optional known page_id so we can skip the lookup
*/
public function addLink( Title $title, $id = null ) {
if ( $title->isExternal() ) {
// Don't record interwikis in pagelinks
$this->addInterwikiLink( $title );
return;
}
$ns = $title->getNamespace();
$dbk = $title->getDBkey();
if ( $ns == NS_MEDIA ) {
// Normalize this pseudo-alias if it makes it down here...
$ns = NS_FILE;
} elseif ( $ns == NS_SPECIAL ) {
// We don't record Special: links currently
// It might actually be wise to, but we'd need to do some normalization.
return;
} elseif ( $dbk === '' ) {
// Don't record self links - [[#Foo]]
return;
}
if ( !isset( $this->mLinks[$ns] ) ) {
$this->mLinks[$ns] = array();
}
if ( is_null( $id ) ) {
$id = $title->getArticleID();
}
$this->mLinks[$ns][$dbk] = $id;
}
/**
* Register a file dependency for this output
* @param string $name Title dbKey
* @param string $timestamp MW timestamp of file creation (or false if non-existing)
* @param string $sha1 Base 36 SHA-1 of file (or false if non-existing)
* @return void
*/
public function addImage( $name, $timestamp = null, $sha1 = null ) {
$this->mImages[$name] = 1;
if ( $timestamp !== null && $sha1 !== null ) {
$this->mFileSearchOptions[$name] = array( 'time' => $timestamp, 'sha1' => $sha1 );
}
}
/**
* Register a template dependency for this output
* @param Title $title
* @param int $page_id
* @param int $rev_id
* @return void
*/
public function addTemplate( $title, $page_id, $rev_id ) {
$ns = $title->getNamespace();
$dbk = $title->getDBkey();
if ( !isset( $this->mTemplates[$ns] ) ) {
$this->mTemplates[$ns] = array();
}
$this->mTemplates[$ns][$dbk] = $page_id;
if ( !isset( $this->mTemplateIds[$ns] ) ) {
$this->mTemplateIds[$ns] = array();
}
$this->mTemplateIds[$ns][$dbk] = $rev_id; // For versioning
}
/**
* @param Title $title Title object, must be an interwiki link
* @throws MWException If given invalid input
*/
public function addInterwikiLink( $title ) {
if ( !$title->isExternal() ) {
throw new MWException( 'Non-interwiki link passed, internal parser error.' );
}
$prefix = $title->getInterwiki();
if ( !isset( $this->mInterwikiLinks[$prefix] ) ) {
$this->mInterwikiLinks[$prefix] = array();
}
$this->mInterwikiLinks[$prefix][$title->getDBkey()] = 1;
}
/**
* Add some text to the "<head>".
* If $tag is set, the section with that tag will only be included once
* in a given page.
* @param string $section
* @param string|bool $tag
*/
public function addHeadItem( $section, $tag = false ) {
if ( $tag !== false ) {
$this->mHeadItems[$tag] = $section;
} else {
$this->mHeadItems[] = $section;
}
}
public function addModules( $modules ) {
$this->mModules = array_merge( $this->mModules, (array)$modules );
}
public function addModuleScripts( $modules ) {
$this->mModuleScripts = array_merge( $this->mModuleScripts, (array)$modules );
}
public function addModuleStyles( $modules ) {
$this->mModuleStyles = array_merge( $this->mModuleStyles, (array)$modules );
}
public function addModuleMessages( $modules ) {
$this->mModuleMessages = array_merge( $this->mModuleMessages, (array)$modules );
}
/**
* Add one or more variables to be set in mw.config in JavaScript.
*
* @param string|array $keys Key or array of key/value pairs.
* @param mixed $value [optional] Value of the configuration variable.
* @since 1.23
*/
public function addJsConfigVars( $keys, $value = null ) {
if ( is_array( $keys ) ) {
foreach ( $keys as $key => $value ) {
$this->mJsConfigVars[$key] = $value;
}
return;
}
$this->mJsConfigVars[$keys] = $value;
}
/**
* Copy items from the OutputPage object into this one
*
* @param OutputPage $out
*/
public function addOutputPageMetadata( OutputPage $out ) {
$this->addModules( $out->getModules() );
$this->addModuleScripts( $out->getModuleScripts() );
$this->addModuleStyles( $out->getModuleStyles() );
$this->addModuleMessages( $out->getModuleMessages() );
$this->addJsConfigVars( $out->getJsConfigVars() );
$this->mHeadItems = array_merge( $this->mHeadItems, $out->getHeadItemsArray() );
$this->mPreventClickjacking = $this->mPreventClickjacking || $out->getPreventClickjacking();
}
/**
* Add a tracking category, getting the title from a system message,
* or print a debug message if the title is invalid.
*
* Please add any message that you use with this function to
* $wgTrackingCategories. That way they will be listed on
* Special:TrackingCategories.
*
* @param string $msg Message key
* @param Title $title title of the page which is being tracked
* @return bool Whether the addition was successful
* @since 1.25
*/
public function addTrackingCategory( $msg, $title ) {
if ( $title->getNamespace() === NS_SPECIAL ) {
wfDebug( __METHOD__ . ": Not adding tracking category $msg to special page!\n" );
return false;
}
// Important to parse with correct title (bug 31469)
$cat = wfMessage( $msg )
->title( $title )
->inContentLanguage()
->text();
# Allow tracking categories to be disabled by setting them to "-"
if ( $cat === '-' ) {
return false;
}
$containerCategory = Title::makeTitleSafe( NS_CATEGORY, $cat );
if ( $containerCategory ) {
$this->addCategory( $containerCategory->getDBkey(), $this->getProperty( 'defaultsort' ) ?: '' );
return true;
} else {
wfDebug( __METHOD__ . ": [[MediaWiki:$msg]] is not a valid title!\n" );
return false;
}
}
/**
* Override the title to be used for display
* -- this is assumed to have been validated
* (check equal normalisation, etc.)
*
* @param string $text Desired title text
*/
public function setDisplayTitle( $text ) {
$this->setTitleText( $text );
$this->setProperty( 'displaytitle', $text );
}
/**
* Get the title to be used for display
*
* @return string
*/
public function getDisplayTitle() {
$t = $this->getTitleText();
if ( $t === '' ) {
return false;
}
return $t;
}
/**
* Fairly generic flag setter thingy.
* @param string $flag
*/
public function setFlag( $flag ) {
$this->mFlags[$flag] = true;
}
public function getFlag( $flag ) {
return isset( $this->mFlags[$flag] );
}
/**
* Set a property to be stored in the page_props database table.
*
* page_props is a key value store indexed by the page ID. This allows
* the parser to set a property on a page which can then be quickly
* retrieved given the page ID or via a DB join when given the page
* title.
*
* Since 1.23, page_props are also indexed by numeric value, to allow
* for efficient "top k" queries of pages wrt a given property.
*
* setProperty() is thus used to propagate properties from the parsed
* page to request contexts other than a page view of the currently parsed
* article.
*
* Some applications examples:
*
* * To implement hidden categories, hiding pages from category listings
* by storing a property.
*
* * Overriding the displayed article title.
* @see ParserOutput::setDisplayTitle()
*
* * To implement image tagging, for example displaying an icon on an
* image thumbnail to indicate that it is listed for deletion on
* Wikimedia Commons.
* This is not actually implemented, yet but would be pretty cool.
*
* @note Do not use setProperty() to set a property which is only used
* in a context where the ParserOutput object itself is already available,
* for example a normal page view. There is no need to save such a property
* in the database since the text is already parsed. You can just hook
* OutputPageParserOutput and get your data out of the ParserOutput object.
*
* If you are writing an extension where you want to set a property in the
* parser which is used by an OutputPageParserOutput hook, you have to
* associate the extension data directly with the ParserOutput object.
* Since MediaWiki 1.21, you can use setExtensionData() to do this:
*
* @par Example:
* @code
* $parser->getOutput()->setExtensionData( 'my_ext_foo', '...' );
* @endcode
*
* And then later, in OutputPageParserOutput or similar:
*
* @par Example:
* @code
* $output->getExtensionData( 'my_ext_foo' );
* @endcode
*
* In MediaWiki 1.20 and older, you have to use a custom member variable
* within the ParserOutput object:
*
* @par Example:
* @code
* $parser->getOutput()->my_ext_foo = '...';
* @endcode
*
*/
public function setProperty( $name, $value ) {
$this->mProperties[$name] = $value;
}
/**
* @param string $name The property name to look up.
*
* @return mixed|bool The value previously set using setProperty(). False if null or no value
* was set for the given property name.
*
* @note You need to use getProperties() to check for boolean and null properties.
*/
public function getProperty( $name ) {
return isset( $this->mProperties[$name] ) ? $this->mProperties[$name] : false;
}
public function unsetProperty( $name ) {
unset( $this->mProperties[$name] );
}
public function getProperties() {
if ( !isset( $this->mProperties ) ) {
$this->mProperties = array();
}
return $this->mProperties;
}
/**
* Returns the options from its ParserOptions which have been taken
* into account to produce this output or false if not available.
* @return array
*/
public function getUsedOptions() {
if ( !isset( $this->mAccessedOptions ) ) {
return array();
}
return array_keys( $this->mAccessedOptions );
}
/**
* Tags a parser option for use in the cache key for this parser output.
* Registered as a watcher at ParserOptions::registerWatcher() by Parser::clearState().
*
* @see ParserCache::getKey
* @see ParserCache::save
* @see ParserOptions::addExtraKey
* @see ParserOptions::optionsHash
* @param string $option
*/
public function recordOption( $option ) {
$this->mAccessedOptions[$option] = true;
}
/**
* Adds an update job to the output. Any update jobs added to the output will
* eventually be executed in order to store any secondary information extracted
* from the page's content. This is triggered by calling getSecondaryDataUpdates()
* and is used for forward links updates on edit and backlink updates by jobs.
*
* @note: custom DataUpdates do not survive serialization of the ParserOutput!
* This is especially relevant when using a cached ParserOutput for updating
* the database, as WikiPage does if $wgAjaxStashEdit is enabled. For this
* reason, ApiStashEdit will skip any ParserOutput that has custom DataUpdates.
*
* @since 1.20
*
* @param DataUpdate $update
*/
public function addSecondaryDataUpdate( DataUpdate $update ) {
$this->mSecondaryDataUpdates[] = $update;
$this->mCustomDataUpdateCount = count( $this->mSecondaryDataUpdates );
}
/**
* Whether this ParserOutput contains custom DataUpdate objects that may not survive
* serialization of the ParserOutput.
*
* @see __sleep()
*
* @return bool
*/
public function hasCustomDataUpdates() {
return ( $this->mCustomDataUpdateCount > 0 );
}
/**
* Returns any DataUpdate jobs to be executed in order to store secondary information
* extracted from the page's content, including a LinksUpdate object for all links stored in
* this ParserOutput object.
*
* @note Avoid using this method directly, use ContentHandler::getSecondaryDataUpdates()
* instead! The content handler may provide additional update objects.
*
* @since 1.20
*
* @param Title $title The title of the page we're updating. If not given, a title object will
* be created based on $this->getTitleText()
* @param bool $recursive Queue jobs for recursive updates?
*
* @throws MWException if called on a ParserOutput instance that was restored from serialization.
* DataUpdates are generally not serializable, so after serialization, they are undefined.
*
* @return array An array of instances of DataUpdate
*/
public function getSecondaryDataUpdates( Title $title = null, $recursive = true ) {
if ( is_null( $title ) ) {
$title = Title::newFromText( $this->getTitleText() );
}
if ( count( $this->mSecondaryDataUpdates ) !== $this->mCustomDataUpdateCount ) {
// NOTE: This happens when mSecondaryDataUpdates are lost during serialization
// (see __sleep below). After (un)serialization, getSecondaryDataUpdates()
// has no defined behavior in that case, and should throw an exception.
throw new MWException( 'getSecondaryDataUpdates() must not be called on ParserOutput restored from serialization.' );
}
// NOTE: ApiStashEdit knows about this "magic" update object. If this goes away,
// ApiStashEdit::buildStashValue needs to be adjusted.
$linksUpdate = new LinksUpdate( $title, $this, $recursive );
return array_merge( $this->mSecondaryDataUpdates, array( $linksUpdate ) );
}
/**
* Attaches arbitrary data to this ParserObject. This can be used to store some information in
* the ParserOutput object for later use during page output. The data will be cached along with
* the ParserOutput object, but unlike data set using setProperty(), it is not recorded in the
* database.
*
* This method is provided to overcome the unsafe practice of attaching extra information to a
* ParserObject by directly assigning member variables.
*
* To use setExtensionData() to pass extension information from a hook inside the parser to a
* hook in the page output, use this in the parser hook:
*
* @par Example:
* @code
* $parser->getOutput()->setExtensionData( 'my_ext_foo', '...' );
* @endcode
*
* And then later, in OutputPageParserOutput or similar:
*
* @par Example:
* @code
* $output->getExtensionData( 'my_ext_foo' );
* @endcode
*
* In MediaWiki 1.20 and older, you have to use a custom member variable
* within the ParserOutput object:
*
* @par Example:
* @code
* $parser->getOutput()->my_ext_foo = '...';
* @endcode
*
* @since 1.21
*
* @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 mixed $value The value to set. Setting a value to null is equivalent to removing
* the value.
*/
public function setExtensionData( $key, $value ) {
if ( $value === null ) {
unset( $this->mExtensionData[$key] );
} else {
$this->mExtensionData[$key] = $value;
}
}
/**
* Gets extensions data previously attached to this ParserOutput using setExtensionData().
* Typically, such data would be set while parsing the page, e.g. by a parser function.
*
* @since 1.21
*
* @param string $key The key to look up.
*
* @return mixed|null The value previously set for the given key using setExtensionData()
* or null if no value was set for this key.
*/
public function getExtensionData( $key ) {
if ( isset( $this->mExtensionData[$key] ) ) {
return $this->mExtensionData[$key];
}
return null;
}
private static function getTimes( $clock = null ) {
$ret = array();
if ( !$clock || $clock === 'wall' ) {
$ret['wall'] = microtime( true );
}
if ( !$clock || $clock === 'cpu' ) {
$ru = wfGetRusage();
if ( $ru ) {
$ret['cpu'] = $ru['ru_utime.tv_sec'] + $ru['ru_utime.tv_usec'] / 1e6;
$ret['cpu'] += $ru['ru_stime.tv_sec'] + $ru['ru_stime.tv_usec'] / 1e6;
}
}
return $ret;
}
/**
* Resets the parse start timestamps for future calls to getTimeSinceStart()
* @since 1.22
*/
public function resetParseStartTime() {
$this->mParseStartTime = self::getTimes();
}
/**
* Returns the time since resetParseStartTime() was last called
*
* Clocks available are:
* - wall: Wall clock time
* - cpu: CPU time (requires getrusage)
*
* @since 1.22
* @param string $clock
* @return float|null
*/
public function getTimeSinceStart( $clock ) {
if ( !isset( $this->mParseStartTime[$clock] ) ) {
return null;
}
$end = self::getTimes( $clock );
return $end[$clock] - $this->mParseStartTime[$clock];
}
/**
* Sets parser limit report data for a key
*
* The key is used as the prefix for various messages used for formatting:
* - $key: The label for the field in the limit report
* - $key-value-text: Message used to format the value in the "NewPP limit
* report" HTML comment. If missing, uses $key-format.
* - $key-value-html: Message used to format the value in the preview
* limit report table. If missing, uses $key-format.
* - $key-value: Message used to format the value. If missing, uses "$1".
*
* Note that all values are interpreted as wikitext, and so should be
* encoded with htmlspecialchars() as necessary, but should avoid complex
* HTML for sanity of display in the "NewPP limit report" comment.
*
* @since 1.22
* @param string $key Message key
* @param mixed $value Appropriate for Message::params()
*/
public function setLimitReportData( $key, $value ) {
$this->mLimitReportData[$key] = $value;
}
/**
* Get or set the prevent-clickjacking flag
*
* @since 1.24
* @param bool|null $flag New flag value, or null to leave it unchanged
* @return bool Old flag value
*/
public function preventClickjacking( $flag = null ) {
return wfSetVar( $this->mPreventClickjacking, $flag );
}
/**
* Save space for serialization by removing useless values
* @return array
*/
public function __sleep() {
return array_diff(
array_keys( get_object_vars( $this ) ),
array( 'mSecondaryDataUpdates', 'mParseStartTime' )
);
}
}