summaryrefslogtreecommitdiff
path: root/includes/parser/ParserOutput.php
diff options
context:
space:
mode:
Diffstat (limited to 'includes/parser/ParserOutput.php')
-rw-r--r--includes/parser/ParserOutput.php208
1 files changed, 177 insertions, 31 deletions
diff --git a/includes/parser/ParserOutput.php b/includes/parser/ParserOutput.php
index 41b4a385..db649f11 100644
--- a/includes/parser/ParserOutput.php
+++ b/includes/parser/ParserOutput.php
@@ -50,7 +50,8 @@ class ParserOutput extends CacheTime {
$mTimestamp; # Timestamp of the revision
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 instances of SecondaryDataObject(), used to cause some information extracted from the page in a custom place.
+ private $mSecondaryDataUpdates = array(); # List of DataUpdate, used to save info from the page somewhere else.
+ private $mExtensionData = array(); # extra data used by extensions
const EDITSECTION_REGEX = '#<(?:mw:)?editsection page="(.*?)" section="(.*?)"(?:/>|>(.*?)(</(?:mw:)?editsection>))#';
@@ -75,18 +76,20 @@ class ParserOutput extends CacheTime {
/**
* callback used by getText to replace editsection tokens
* @private
+ * @param $m
+ * @throws MWException
* @return mixed
*/
function replaceEditSectionLinksCallback( $m ) {
global $wgOut, $wgLang;
$args = array(
- htmlspecialchars_decode($m[1]),
- htmlspecialchars_decode($m[2]),
- isset($m[4]) ? $m[3] : null,
+ htmlspecialchars_decode( $m[1] ),
+ htmlspecialchars_decode( $m[2] ),
+ isset( $m[4] ) ? $m[3] : null,
);
$args[0] = Title::newFromText( $args[0] );
- if ( !is_object($args[0]) ) {
- throw new MWException("Bad parser output text.");
+ if ( !is_object( $args[0] ) ) {
+ throw new MWException( "Bad parser output text." );
}
$args[] = $wgLang->getCode();
$skin = $wgOut->getSkin();
@@ -150,11 +153,35 @@ class ParserOutput extends CacheTime {
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
+ */
+ 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
+ );
+ }
+
function addExternalLink( $url ) {
# We don't register links pointing to our own server, unless... :-)
global $wgServer, $wgRegisterInternalExternals;
- if( $wgRegisterInternalExternals or stripos($url,$wgServer.'/')!==0)
+
+ $registerExternalLink = true;
+ if( !$wgRegisterInternalExternals ) {
+ $registerExternalLink = !self::isLinkInternal( $wgServer, $url );
+ }
+ if( $registerExternalLink ) {
$this->mExternalLinks[$url] = 1;
+ }
}
/**
@@ -163,7 +190,7 @@ class ParserOutput extends CacheTime {
* @param $title Title object
* @param $id Mixed: optional known page_id so we can skip the lookup
*/
- function addLink( $title, $id = null ) {
+ function addLink( Title $title, $id = null ) {
if ( $title->isExternal() ) {
// Don't record interwikis in pagelinks
$this->addInterwikiLink( $title );
@@ -193,9 +220,9 @@ class ParserOutput extends CacheTime {
/**
* Register a file dependency for this output
- * @param $name string Title dbKey
- * @param $timestamp string MW timestamp of file creation (or false if non-existing)
- * @param $sha1 string base 36 SHA-1 of file (or false if non-existing)
+ * @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
*/
function addImage( $name, $timestamp = null, $sha1 = null ) {
@@ -234,7 +261,7 @@ class ParserOutput extends CacheTime {
if( $prefix == '' ) {
throw new MWException( 'Non-interwiki link passed, internal parser error.' );
}
- if (!isset($this->mInterwikiLinks[$prefix])) {
+ if ( !isset( $this->mInterwikiLinks[$prefix] ) ) {
$this->mInterwikiLinks[$prefix] = array();
}
$this->mInterwikiLinks[$prefix][$title->getDBkey()] = 1;
@@ -288,7 +315,7 @@ class ParserOutput extends CacheTime {
* -- this is assumed to have been validated
* (check equal normalisation, etc.)
*
- * @param $text String: desired title text
+ * @param string $text desired title text
*/
public function setDisplayTitle( $text ) {
$this->setTitleText( $text );
@@ -320,13 +347,67 @@ class ParserOutput extends CacheTime {
}
/**
- * Set a property to be cached in the DB
+ * 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.
+ *
+ * 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 it 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;
}
- public function getProperty( $name ){
+ public function getProperty( $name ) {
return isset( $this->mProperties[$name] ) ? $this->mProperties[$name] : false;
}
@@ -337,26 +418,25 @@ class ParserOutput extends CacheTime {
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 mixed Array
*/
- public function getUsedOptions() {
+ public function getUsedOptions() {
if ( !isset( $this->mAccessedOptions ) ) {
return array();
}
return array_keys( $this->mAccessedOptions );
- }
+ }
- /**
- * Callback passed by the Parser to the ParserOptions to keep track of which options are used.
- * @access private
- */
- function recordOption( $option ) {
- $this->mAccessedOptions[$option] = true;
- }
+ /**
+ * Callback passed by the Parser to the ParserOptions to keep track of which options are used.
+ * @access private
+ */
+ function recordOption( $option ) {
+ $this->mAccessedOptions[$option] = true;
+ }
/**
* Adds an update job to the output. Any update jobs added to the output will eventually bexecuted in order to
@@ -375,9 +455,13 @@ class ParserOutput extends CacheTime {
* 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 of the page we're updating. If not given, a title object will be created based on $this->getTitleText()
+ * @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 $recursive Boolean: queue jobs for recursive updates?
*
* @return Array. An array of instances of DataUpdate
@@ -389,13 +473,75 @@ class ParserOutput extends CacheTime {
$linksUpdate = new LinksUpdate( $title, $this, $recursive );
- if ( $this->mSecondaryDataUpdates === array() ) {
- return array( $linksUpdate );
+ 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 {
- $updates = array_merge( $this->mSecondaryDataUpdates, array( $linksUpdate ) );
+ $this->mExtensionData[$key] = $value;
}
+ }
- return $updates;
- }
+ /**
+ * 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 The value previously set for the given key using setExtensionData( $key ),
+ * 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;
+ }
}