MediaWiki  REL1_22
ParserOutput.php
Go to the documentation of this file.
00001 <?php
00002 
00024 class ParserOutput extends CacheTime {
00025     var $mText,                       # The output text
00026         $mLanguageLinks,              # List of the full text of language links, in the order they appear
00027         $mCategories,                 # Map of category names to sort keys
00028         $mTitleText,                  # title text of the chosen language variant
00029         $mLinks = array(),            # 2-D map of NS/DBK to ID for the links in the document. ID=zero for broken.
00030         $mTemplates = array(),        # 2-D map of NS/DBK to ID for the template references. ID=zero for broken.
00031         $mTemplateIds = array(),      # 2-D map of NS/DBK to rev ID for the template references. ID=zero for broken.
00032         $mImages = array(),           # DB keys of the images used, in the array key only
00033         $mFileSearchOptions = array(), # DB keys of the images used mapped to sha1 and MW timestamp
00034         $mExternalLinks = array(),    # External link URLs, in the key only
00035         $mInterwikiLinks = array(),   # 2-D map of prefix/DBK (in keys only) for the inline interwiki links in the document.
00036         $mNewSection = false,         # Show a new section link?
00037         $mHideNewSection = false,     # Hide the new section link?
00038         $mNoGallery = false,          # No gallery on category page? (__NOGALLERY__)
00039         $mHeadItems = array(),        # Items to put in the <head> section
00040         $mModules = array(),          # Modules to be loaded by the resource loader
00041         $mModuleScripts = array(),    # Modules of which only the JS will be loaded by the resource loader
00042         $mModuleStyles = array(),     # Modules of which only the CSSS will be loaded by the resource loader
00043         $mModuleMessages = array(),   # Modules of which only the messages will be loaded by the resource loader
00044         $mOutputHooks = array(),      # Hook tags as per $wgParserOutputHooks
00045         $mWarnings = array(),         # Warning text to be returned to the user. Wikitext formatted, in the key only
00046         $mSections = array(),         # Table of contents
00047         $mEditSectionTokens = false,  # prefix/suffix markers if edit sections were output as tokens
00048         $mProperties = array(),       # Name/value pairs to be cached in the DB
00049         $mTOCHTML = '',               # HTML of the TOC
00050         $mTimestamp,                  # Timestamp of the revision
00051         $mTOCEnabled = true;          # Whether TOC should be shown, can't override __NOTOC__
00052         private $mIndexPolicy = '';       # 'index' or 'noindex'?  Any other value will result in no change.
00053         private $mAccessedOptions = array(); # List of ParserOptions (stored in the keys)
00054         private $mSecondaryDataUpdates = array(); # List of DataUpdate, used to save info from the page somewhere else.
00055         private $mExtensionData = array(); # extra data used by extensions
00056         private $mLimitReportData = array(); # Parser limit report data
00057         private $mParseStartTime = array(); # Timestamps for getTimeSinceStart()
00058         private $mPreventClickjacking = false; # Whether to emit X-Frame-Options: DENY
00059 
00060     const EDITSECTION_REGEX = '#<(?:mw:)?editsection page="(.*?)" section="(.*?)"(?:/>|>(.*?)(</(?:mw:)?editsection>))#';
00061 
00062     function __construct( $text = '', $languageLinks = array(), $categoryLinks = array(),
00063         $containsOldMagic = false, $titletext = '' )
00064     {
00065         $this->mText = $text;
00066         $this->mLanguageLinks = $languageLinks;
00067         $this->mCategories = $categoryLinks;
00068         $this->mContainsOldMagic = $containsOldMagic;
00069         $this->mTitleText = $titletext;
00070     }
00071 
00072     function getText() {
00073         wfProfileIn( __METHOD__ );
00074         $text = $this->mText;
00075         if ( $this->mEditSectionTokens ) {
00076             $text = preg_replace_callback( ParserOutput::EDITSECTION_REGEX,
00077                 array( &$this, 'replaceEditSectionLinksCallback' ), $text );
00078         } else {
00079             $text = preg_replace( ParserOutput::EDITSECTION_REGEX, '', $text );
00080         }
00081 
00082         // If you have an old cached version of this class - sorry, you can't disable the TOC
00083         if ( isset( $this->mTOCEnabled ) && $this->mTOCEnabled ) {
00084             $text = str_replace( array( Parser::TOC_START, Parser::TOC_END ), '', $text );
00085         } else {
00086             $text = preg_replace(
00087                 '#'. preg_quote( Parser::TOC_START ) . '.*?' . preg_quote( Parser::TOC_END ) . '#s',
00088                 '',
00089                 $text
00090             );
00091         }
00092         wfProfileOut( __METHOD__ );
00093         return $text;
00094     }
00095 
00103     function replaceEditSectionLinksCallback( $m ) {
00104         global $wgOut, $wgLang;
00105         $args = array(
00106             htmlspecialchars_decode( $m[1] ),
00107             htmlspecialchars_decode( $m[2] ),
00108             isset( $m[4] ) ? $m[3] : null,
00109         );
00110         $args[0] = Title::newFromText( $args[0] );
00111         if ( !is_object( $args[0] ) ) {
00112             throw new MWException( "Bad parser output text." );
00113         }
00114         $args[] = $wgLang->getCode();
00115         $skin = $wgOut->getSkin();
00116         return call_user_func_array( array( $skin, 'doEditSectionLink' ), $args );
00117     }
00118 
00119     function &getLanguageLinks()         { return $this->mLanguageLinks; }
00120     function getInterwikiLinks()         { return $this->mInterwikiLinks; }
00121     function getCategoryLinks()          { return array_keys( $this->mCategories ); }
00122     function &getCategories()            { return $this->mCategories; }
00123     function getTitleText()              { return $this->mTitleText; }
00124     function getSections()               { return $this->mSections; }
00125     function getEditSectionTokens()      { return $this->mEditSectionTokens; }
00126     function &getLinks()                 { return $this->mLinks; }
00127     function &getTemplates()             { return $this->mTemplates; }
00128     function &getTemplateIds()           { return $this->mTemplateIds; }
00129     function &getImages()                { return $this->mImages; }
00130     function &getFileSearchOptions()     { return $this->mFileSearchOptions; }
00131     function &getExternalLinks()         { return $this->mExternalLinks; }
00132     function getNoGallery()              { return $this->mNoGallery; }
00133     function getHeadItems()              { return $this->mHeadItems; }
00134     function getModules()                { return $this->mModules; }
00135     function getModuleScripts()          { return $this->mModuleScripts; }
00136     function getModuleStyles()           { return $this->mModuleStyles; }
00137     function getModuleMessages()         { return $this->mModuleMessages; }
00138     function getOutputHooks()            { return (array)$this->mOutputHooks; }
00139     function getWarnings()               { return array_keys( $this->mWarnings ); }
00140     function getIndexPolicy()            { return $this->mIndexPolicy; }
00141     function getTOCHTML()                { return $this->mTOCHTML; }
00142     function getTimestamp()              { return $this->mTimestamp; }
00143     function getLimitReportData()        { return $this->mLimitReportData; }
00144     function getTOCEnabled()             { return $this->mTOCEnabled; }
00145 
00146     function setText( $text )            { return wfSetVar( $this->mText, $text ); }
00147     function setLanguageLinks( $ll )     { return wfSetVar( $this->mLanguageLinks, $ll ); }
00148     function setCategoryLinks( $cl )     { return wfSetVar( $this->mCategories, $cl ); }
00149 
00150     function setTitleText( $t )          { return wfSetVar( $this->mTitleText, $t ); }
00151     function setSections( $toc )         { return wfSetVar( $this->mSections, $toc ); }
00152     function setEditSectionTokens( $t )  { return wfSetVar( $this->mEditSectionTokens, $t ); }
00153     function setIndexPolicy( $policy )   { return wfSetVar( $this->mIndexPolicy, $policy ); }
00154     function setTOCHTML( $tochtml )      { return wfSetVar( $this->mTOCHTML, $tochtml ); }
00155     function setTimestamp( $timestamp )  { return wfSetVar( $this->mTimestamp, $timestamp ); }
00156     function setTOCEnabled( $flag )      { return wfSetVar( $this->mTOCEnabled, $flag ); }
00157 
00158     function addCategory( $c, $sort )    { $this->mCategories[$c] = $sort; }
00159     function addLanguageLink( $t )       { $this->mLanguageLinks[] = $t; }
00160     function addWarning( $s )            { $this->mWarnings[$s] = 1; }
00161 
00162     function addOutputHook( $hook, $data = false ) {
00163         $this->mOutputHooks[] = array( $hook, $data );
00164     }
00165 
00166     function setNewSection( $value ) {
00167         $this->mNewSection = (bool)$value;
00168     }
00169     function hideNewSection( $value ) {
00170         $this->mHideNewSection = (bool)$value;
00171     }
00172     function getHideNewSection() {
00173         return (bool)$this->mHideNewSection;
00174     }
00175     function getNewSection() {
00176         return (bool)$this->mNewSection;
00177     }
00178 
00186     static function isLinkInternal( $internal, $url ) {
00187         return (bool)preg_match( '/^' .
00188             # If server is proto relative, check also for http/https links
00189             ( substr( $internal, 0, 2 ) === '//' ? '(?:https?:)?' : '' ) .
00190             preg_quote( $internal, '/' ) .
00191             # check for query/path/anchor or end of link in each case
00192             '(?:[\?\/\#]|$)/i',
00193             $url
00194         );
00195     }
00196 
00197     function addExternalLink( $url ) {
00198         # We don't register links pointing to our own server, unless... :-)
00199         global $wgServer, $wgRegisterInternalExternals;
00200 
00201         $registerExternalLink = true;
00202         if ( !$wgRegisterInternalExternals ) {
00203             $registerExternalLink = !self::isLinkInternal( $wgServer, $url );
00204         }
00205         if ( $registerExternalLink ) {
00206             $this->mExternalLinks[$url] = 1;
00207         }
00208     }
00209 
00216     function addLink( Title $title, $id = null ) {
00217         if ( $title->isExternal() ) {
00218             // Don't record interwikis in pagelinks
00219             $this->addInterwikiLink( $title );
00220             return;
00221         }
00222         $ns = $title->getNamespace();
00223         $dbk = $title->getDBkey();
00224         if ( $ns == NS_MEDIA ) {
00225             // Normalize this pseudo-alias if it makes it down here...
00226             $ns = NS_FILE;
00227         } elseif ( $ns == NS_SPECIAL ) {
00228             // We don't record Special: links currently
00229             // It might actually be wise to, but we'd need to do some normalization.
00230             return;
00231         } elseif ( $dbk === '' ) {
00232             // Don't record self links -  [[#Foo]]
00233             return;
00234         }
00235         if ( !isset( $this->mLinks[$ns] ) ) {
00236             $this->mLinks[$ns] = array();
00237         }
00238         if ( is_null( $id ) ) {
00239             $id = $title->getArticleID();
00240         }
00241         $this->mLinks[$ns][$dbk] = $id;
00242     }
00243 
00251     function addImage( $name, $timestamp = null, $sha1 = null ) {
00252         $this->mImages[$name] = 1;
00253         if ( $timestamp !== null && $sha1 !== null ) {
00254             $this->mFileSearchOptions[$name] = array( 'time' => $timestamp, 'sha1' => $sha1 );
00255         }
00256     }
00257 
00265     function addTemplate( $title, $page_id, $rev_id ) {
00266         $ns = $title->getNamespace();
00267         $dbk = $title->getDBkey();
00268         if ( !isset( $this->mTemplates[$ns] ) ) {
00269             $this->mTemplates[$ns] = array();
00270         }
00271         $this->mTemplates[$ns][$dbk] = $page_id;
00272         if ( !isset( $this->mTemplateIds[$ns] ) ) {
00273             $this->mTemplateIds[$ns] = array();
00274         }
00275         $this->mTemplateIds[$ns][$dbk] = $rev_id; // For versioning
00276     }
00277 
00282     function addInterwikiLink( $title ) {
00283         $prefix = $title->getInterwiki();
00284         if ( $prefix == '' ) {
00285             throw new MWException( 'Non-interwiki link passed, internal parser error.' );
00286         }
00287         if ( !isset( $this->mInterwikiLinks[$prefix] ) ) {
00288             $this->mInterwikiLinks[$prefix] = array();
00289         }
00290         $this->mInterwikiLinks[$prefix][$title->getDBkey()] = 1;
00291     }
00292 
00298     function addHeadItem( $section, $tag = false ) {
00299         if ( $tag !== false ) {
00300             $this->mHeadItems[$tag] = $section;
00301         } else {
00302             $this->mHeadItems[] = $section;
00303         }
00304     }
00305 
00306     public function addModules( $modules ) {
00307         $this->mModules = array_merge( $this->mModules, (array)$modules );
00308     }
00309 
00310     public function addModuleScripts( $modules ) {
00311         $this->mModuleScripts = array_merge( $this->mModuleScripts, (array)$modules );
00312     }
00313 
00314     public function addModuleStyles( $modules ) {
00315         $this->mModuleStyles = array_merge( $this->mModuleStyles, (array)$modules );
00316     }
00317 
00318     public function addModuleMessages( $modules ) {
00319         $this->mModuleMessages = array_merge( $this->mModuleMessages, (array)$modules );
00320     }
00321 
00327     public function addOutputPageMetadata( OutputPage $out ) {
00328         $this->addModules( $out->getModules() );
00329         $this->addModuleScripts( $out->getModuleScripts() );
00330         $this->addModuleStyles( $out->getModuleStyles() );
00331         $this->addModuleMessages( $out->getModuleMessages() );
00332 
00333         $this->mHeadItems = array_merge( $this->mHeadItems, $out->getHeadItemsArray() );
00334         $this->mPreventClickjacking = $this->mPreventClickjacking || $out->getPreventClickjacking();
00335     }
00336 
00344     public function setDisplayTitle( $text ) {
00345         $this->setTitleText( $text );
00346         $this->setProperty( 'displaytitle', $text );
00347     }
00348 
00354     public function getDisplayTitle() {
00355         $t = $this->getTitleText();
00356         if ( $t === '' ) {
00357             return false;
00358         }
00359         return $t;
00360     }
00361 
00365     public function setFlag( $flag ) {
00366         $this->mFlags[$flag] = true;
00367     }
00368 
00369     public function getFlag( $flag ) {
00370         return isset( $this->mFlags[$flag] );
00371     }
00372 
00430     public function setProperty( $name, $value ) {
00431         $this->mProperties[$name] = $value;
00432     }
00433 
00434     public function getProperty( $name ) {
00435         return isset( $this->mProperties[$name] ) ? $this->mProperties[$name] : false;
00436     }
00437 
00438     public function getProperties() {
00439         if ( !isset( $this->mProperties ) ) {
00440             $this->mProperties = array();
00441         }
00442         return $this->mProperties;
00443     }
00444 
00450     public function getUsedOptions() {
00451         if ( !isset( $this->mAccessedOptions ) ) {
00452             return array();
00453         }
00454         return array_keys( $this->mAccessedOptions );
00455     }
00456 
00461     function recordOption( $option ) {
00462         $this->mAccessedOptions[$option] = true;
00463     }
00464 
00473     public function addSecondaryDataUpdate( DataUpdate $update ) {
00474         $this->mSecondaryDataUpdates[] = $update;
00475     }
00476 
00493     public function getSecondaryDataUpdates( Title $title = null, $recursive = true ) {
00494         if ( is_null( $title ) ) {
00495             $title = Title::newFromText( $this->getTitleText() );
00496         }
00497 
00498         $linksUpdate = new LinksUpdate( $title, $this, $recursive );
00499 
00500         return array_merge( $this->mSecondaryDataUpdates, array( $linksUpdate ) );
00501     }
00502 
00544     public function setExtensionData( $key, $value ) {
00545         if ( $value === null ) {
00546             unset( $this->mExtensionData[$key] );
00547         } else {
00548             $this->mExtensionData[$key] = $value;
00549         }
00550     }
00551 
00563     public function getExtensionData( $key ) {
00564         if ( isset( $this->mExtensionData[$key] ) ) {
00565             return $this->mExtensionData[$key];
00566         }
00567 
00568         return null;
00569     }
00570 
00571     private static function getTimes( $clock = null ) {
00572         $ret = array();
00573         if ( !$clock || $clock === 'wall' ) {
00574             $ret['wall'] = microtime( true );
00575         }
00576         if ( ( !$clock || $clock === 'cpu' ) && function_exists( 'getrusage' ) ) {
00577             $ru = getrusage();
00578             $ret['cpu'] = $ru['ru_utime.tv_sec'] + $ru['ru_utime.tv_usec'] / 1e6;
00579             $ret['cpu'] += $ru['ru_stime.tv_sec'] + $ru['ru_stime.tv_usec'] / 1e6;
00580         }
00581         return $ret;
00582     }
00583 
00588     function resetParseStartTime() {
00589         $this->mParseStartTime = self::getTimes();
00590     }
00591 
00603     function getTimeSinceStart( $clock ) {
00604         if ( !isset( $this->mParseStartTime[$clock] ) ) {
00605             return null;
00606         }
00607 
00608         $end = self::getTimes( $clock );
00609         return $end[$clock] - $this->mParseStartTime[$clock];
00610     }
00611 
00631     function setLimitReportData( $key, $value ) {
00632         $this->mLimitReportData[$key] = $value;
00633     }
00634 
00642     public function preventClickjacking( $flag = null ) {
00643         return wfSetVar( $this->mPreventClickjacking, $flag );
00644     }
00645 }