MediaWiki  REL1_23
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         $mJsConfigVars = array(),     # JavaScript config variable for mw.config combined with this page
00045         $mOutputHooks = array(),      # Hook tags as per $wgParserOutputHooks
00046         $mWarnings = array(),         # Warning text to be returned to the user. Wikitext formatted, in the key only
00047         $mSections = array(),         # Table of contents
00048         $mEditSectionTokens = false,  # prefix/suffix markers if edit sections were output as tokens
00049         $mProperties = array(),       # Name/value pairs to be cached in the DB
00050         $mTOCHTML = '',               # HTML of the TOC
00051         $mTimestamp,                  # Timestamp of the revision
00052         $mTOCEnabled = true;          # Whether TOC should be shown, can't override __NOTOC__
00053         private $mIndexPolicy = '';       # 'index' or 'noindex'?  Any other value will result in no change.
00054         private $mAccessedOptions = array(); # List of ParserOptions (stored in the keys)
00055         private $mSecondaryDataUpdates = array(); # List of DataUpdate, used to save info from the page somewhere else.
00056         private $mExtensionData = array(); # extra data used by extensions
00057         private $mLimitReportData = array(); # Parser limit report data
00058         private $mParseStartTime = array(); # Timestamps for getTimeSinceStart()
00059         private $mPreventClickjacking = false; # Whether to emit X-Frame-Options: DENY
00060 
00061     const EDITSECTION_REGEX = '#<(?:mw:)?editsection page="(.*?)" section="(.*?)"(?:/>|>(.*?)(</(?:mw:)?editsection>))#';
00062 
00063     function __construct( $text = '', $languageLinks = array(), $categoryLinks = array(),
00064         $containsOldMagic = false, $titletext = ''
00065     ) {
00066         $this->mText = $text;
00067         $this->mLanguageLinks = $languageLinks;
00068         $this->mCategories = $categoryLinks;
00069         $this->mContainsOldMagic = $containsOldMagic;
00070         $this->mTitleText = $titletext;
00071     }
00072 
00073     function getText() {
00074         wfProfileIn( __METHOD__ );
00075         $text = $this->mText;
00076         if ( $this->mEditSectionTokens ) {
00077             $text = preg_replace_callback( ParserOutput::EDITSECTION_REGEX,
00078                 array( &$this, 'replaceEditSectionLinksCallback' ), $text );
00079         } else {
00080             $text = preg_replace( ParserOutput::EDITSECTION_REGEX, '', $text );
00081         }
00082 
00083         // If you have an old cached version of this class - sorry, you can't disable the TOC
00084         if ( isset( $this->mTOCEnabled ) && $this->mTOCEnabled ) {
00085             $text = str_replace( array( Parser::TOC_START, Parser::TOC_END ), '', $text );
00086         } else {
00087             $text = preg_replace(
00088                 '#' . preg_quote( Parser::TOC_START ) . '.*?' . preg_quote( Parser::TOC_END ) . '#s',
00089                 '',
00090                 $text
00091             );
00092         }
00093         wfProfileOut( __METHOD__ );
00094         return $text;
00095     }
00096 
00104     function replaceEditSectionLinksCallback( $m ) {
00105         global $wgOut, $wgLang;
00106         $args = array(
00107             htmlspecialchars_decode( $m[1] ),
00108             htmlspecialchars_decode( $m[2] ),
00109             isset( $m[4] ) ? $m[3] : null,
00110         );
00111         $args[0] = Title::newFromText( $args[0] );
00112         if ( !is_object( $args[0] ) ) {
00113             throw new MWException( "Bad parser output text." );
00114         }
00115         $args[] = $wgLang->getCode();
00116         $skin = $wgOut->getSkin();
00117         return call_user_func_array( array( $skin, 'doEditSectionLink' ), $args );
00118     }
00119 
00120     function &getLanguageLinks()         { return $this->mLanguageLinks; }
00121     function getInterwikiLinks()         { return $this->mInterwikiLinks; }
00122     function getCategoryLinks()          { return array_keys( $this->mCategories ); }
00123     function &getCategories()            { return $this->mCategories; }
00124     function getTitleText()              { return $this->mTitleText; }
00125     function getSections()               { return $this->mSections; }
00126     function getEditSectionTokens()      { return $this->mEditSectionTokens; }
00127     function &getLinks()                 { return $this->mLinks; }
00128     function &getTemplates()             { return $this->mTemplates; }
00129     function &getTemplateIds()           { return $this->mTemplateIds; }
00130     function &getImages()                { return $this->mImages; }
00131     function &getFileSearchOptions()     { return $this->mFileSearchOptions; }
00132     function &getExternalLinks()         { return $this->mExternalLinks; }
00133     function getNoGallery()              { return $this->mNoGallery; }
00134     function getHeadItems()              { return $this->mHeadItems; }
00135     function getModules()                { return $this->mModules; }
00136     function getModuleScripts()          { return $this->mModuleScripts; }
00137     function getModuleStyles()           { return $this->mModuleStyles; }
00138     function getModuleMessages()         { return $this->mModuleMessages; }
00140     function getJsConfigVars()           { return $this->mJsConfigVars; }
00141     function getOutputHooks()            { return (array)$this->mOutputHooks; }
00142     function getWarnings()               { return array_keys( $this->mWarnings ); }
00143     function getIndexPolicy()            { return $this->mIndexPolicy; }
00144     function getTOCHTML()                { return $this->mTOCHTML; }
00145     function getTimestamp()              { return $this->mTimestamp; }
00146     function getLimitReportData()        { return $this->mLimitReportData; }
00147     function getTOCEnabled()             { return $this->mTOCEnabled; }
00148 
00149     function setText( $text )            { return wfSetVar( $this->mText, $text ); }
00150     function setLanguageLinks( $ll )     { return wfSetVar( $this->mLanguageLinks, $ll ); }
00151     function setCategoryLinks( $cl )     { return wfSetVar( $this->mCategories, $cl ); }
00152 
00153     function setTitleText( $t )          { return wfSetVar( $this->mTitleText, $t ); }
00154     function setSections( $toc )         { return wfSetVar( $this->mSections, $toc ); }
00155     function setEditSectionTokens( $t )  { return wfSetVar( $this->mEditSectionTokens, $t ); }
00156     function setIndexPolicy( $policy )   { return wfSetVar( $this->mIndexPolicy, $policy ); }
00157     function setTOCHTML( $tochtml )      { return wfSetVar( $this->mTOCHTML, $tochtml ); }
00158     function setTimestamp( $timestamp )  { return wfSetVar( $this->mTimestamp, $timestamp ); }
00159     function setTOCEnabled( $flag )      { return wfSetVar( $this->mTOCEnabled, $flag ); }
00160 
00161     function addCategory( $c, $sort )    { $this->mCategories[$c] = $sort; }
00162     function addLanguageLink( $t )       { $this->mLanguageLinks[] = $t; }
00163     function addWarning( $s )            { $this->mWarnings[$s] = 1; }
00164 
00165     function addOutputHook( $hook, $data = false ) {
00166         $this->mOutputHooks[] = array( $hook, $data );
00167     }
00168 
00169     function setNewSection( $value ) {
00170         $this->mNewSection = (bool)$value;
00171     }
00172     function hideNewSection( $value ) {
00173         $this->mHideNewSection = (bool)$value;
00174     }
00175     function getHideNewSection() {
00176         return (bool)$this->mHideNewSection;
00177     }
00178     function getNewSection() {
00179         return (bool)$this->mNewSection;
00180     }
00181 
00189     static function isLinkInternal( $internal, $url ) {
00190         return (bool)preg_match( '/^' .
00191             # If server is proto relative, check also for http/https links
00192             ( substr( $internal, 0, 2 ) === '//' ? '(?:https?:)?' : '' ) .
00193             preg_quote( $internal, '/' ) .
00194             # check for query/path/anchor or end of link in each case
00195             '(?:[\?\/\#]|$)/i',
00196             $url
00197         );
00198     }
00199 
00200     function addExternalLink( $url ) {
00201         # We don't register links pointing to our own server, unless... :-)
00202         global $wgServer, $wgRegisterInternalExternals;
00203 
00204         $registerExternalLink = true;
00205         if ( !$wgRegisterInternalExternals ) {
00206             $registerExternalLink = !self::isLinkInternal( $wgServer, $url );
00207         }
00208         if ( $registerExternalLink ) {
00209             $this->mExternalLinks[$url] = 1;
00210         }
00211     }
00212 
00219     function addLink( Title $title, $id = null ) {
00220         if ( $title->isExternal() ) {
00221             // Don't record interwikis in pagelinks
00222             $this->addInterwikiLink( $title );
00223             return;
00224         }
00225         $ns = $title->getNamespace();
00226         $dbk = $title->getDBkey();
00227         if ( $ns == NS_MEDIA ) {
00228             // Normalize this pseudo-alias if it makes it down here...
00229             $ns = NS_FILE;
00230         } elseif ( $ns == NS_SPECIAL ) {
00231             // We don't record Special: links currently
00232             // It might actually be wise to, but we'd need to do some normalization.
00233             return;
00234         } elseif ( $dbk === '' ) {
00235             // Don't record self links -  [[#Foo]]
00236             return;
00237         }
00238         if ( !isset( $this->mLinks[$ns] ) ) {
00239             $this->mLinks[$ns] = array();
00240         }
00241         if ( is_null( $id ) ) {
00242             $id = $title->getArticleID();
00243         }
00244         $this->mLinks[$ns][$dbk] = $id;
00245     }
00246 
00254     function addImage( $name, $timestamp = null, $sha1 = null ) {
00255         $this->mImages[$name] = 1;
00256         if ( $timestamp !== null && $sha1 !== null ) {
00257             $this->mFileSearchOptions[$name] = array( 'time' => $timestamp, 'sha1' => $sha1 );
00258         }
00259     }
00260 
00268     function addTemplate( $title, $page_id, $rev_id ) {
00269         $ns = $title->getNamespace();
00270         $dbk = $title->getDBkey();
00271         if ( !isset( $this->mTemplates[$ns] ) ) {
00272             $this->mTemplates[$ns] = array();
00273         }
00274         $this->mTemplates[$ns][$dbk] = $page_id;
00275         if ( !isset( $this->mTemplateIds[$ns] ) ) {
00276             $this->mTemplateIds[$ns] = array();
00277         }
00278         $this->mTemplateIds[$ns][$dbk] = $rev_id; // For versioning
00279     }
00280 
00285     function addInterwikiLink( $title ) {
00286         if ( !$title->isExternal() ) {
00287             throw new MWException( 'Non-interwiki link passed, internal parser error.' );
00288         }
00289         $prefix = $title->getInterwiki();
00290         if ( !isset( $this->mInterwikiLinks[$prefix] ) ) {
00291             $this->mInterwikiLinks[$prefix] = array();
00292         }
00293         $this->mInterwikiLinks[$prefix][$title->getDBkey()] = 1;
00294     }
00295 
00301     function addHeadItem( $section, $tag = false ) {
00302         if ( $tag !== false ) {
00303             $this->mHeadItems[$tag] = $section;
00304         } else {
00305             $this->mHeadItems[] = $section;
00306         }
00307     }
00308 
00309     public function addModules( $modules ) {
00310         $this->mModules = array_merge( $this->mModules, (array)$modules );
00311     }
00312 
00313     public function addModuleScripts( $modules ) {
00314         $this->mModuleScripts = array_merge( $this->mModuleScripts, (array)$modules );
00315     }
00316 
00317     public function addModuleStyles( $modules ) {
00318         $this->mModuleStyles = array_merge( $this->mModuleStyles, (array)$modules );
00319     }
00320 
00321     public function addModuleMessages( $modules ) {
00322         $this->mModuleMessages = array_merge( $this->mModuleMessages, (array)$modules );
00323     }
00324 
00332     public function addJsConfigVars( $keys, $value = null ) {
00333         if ( is_array( $keys ) ) {
00334             foreach ( $keys as $key => $value ) {
00335                 $this->mJsConfigVars[$key] = $value;
00336             }
00337             return;
00338         }
00339 
00340         $this->mJsConfigVars[$keys] = $value;
00341     }
00342 
00348     public function addOutputPageMetadata( OutputPage $out ) {
00349         $this->addModules( $out->getModules() );
00350         $this->addModuleScripts( $out->getModuleScripts() );
00351         $this->addModuleStyles( $out->getModuleStyles() );
00352         $this->addModuleMessages( $out->getModuleMessages() );
00353         $this->addJsConfigVars( $out->getJsConfigVars() );
00354 
00355         $this->mHeadItems = array_merge( $this->mHeadItems, $out->getHeadItemsArray() );
00356         $this->mPreventClickjacking = $this->mPreventClickjacking || $out->getPreventClickjacking();
00357     }
00358 
00366     public function setDisplayTitle( $text ) {
00367         $this->setTitleText( $text );
00368         $this->setProperty( 'displaytitle', $text );
00369     }
00370 
00376     public function getDisplayTitle() {
00377         $t = $this->getTitleText();
00378         if ( $t === '' ) {
00379             return false;
00380         }
00381         return $t;
00382     }
00383 
00387     public function setFlag( $flag ) {
00388         $this->mFlags[$flag] = true;
00389     }
00390 
00391     public function getFlag( $flag ) {
00392         return isset( $this->mFlags[$flag] );
00393     }
00394 
00452     public function setProperty( $name, $value ) {
00453         $this->mProperties[$name] = $value;
00454     }
00455 
00456     public function getProperty( $name ) {
00457         return isset( $this->mProperties[$name] ) ? $this->mProperties[$name] : false;
00458     }
00459 
00460     public function getProperties() {
00461         if ( !isset( $this->mProperties ) ) {
00462             $this->mProperties = array();
00463         }
00464         return $this->mProperties;
00465     }
00466 
00472     public function getUsedOptions() {
00473         if ( !isset( $this->mAccessedOptions ) ) {
00474             return array();
00475         }
00476         return array_keys( $this->mAccessedOptions );
00477     }
00478 
00488     public function recordOption( $option ) {
00489         $this->mAccessedOptions[$option] = true;
00490     }
00491 
00502     public function addSecondaryDataUpdate( DataUpdate $update ) {
00503         $this->mSecondaryDataUpdates[] = $update;
00504     }
00505 
00522     public function getSecondaryDataUpdates( Title $title = null, $recursive = true ) {
00523         if ( is_null( $title ) ) {
00524             $title = Title::newFromText( $this->getTitleText() );
00525         }
00526 
00527         $linksUpdate = new LinksUpdate( $title, $this, $recursive );
00528 
00529         return array_merge( $this->mSecondaryDataUpdates, array( $linksUpdate ) );
00530     }
00531 
00573     public function setExtensionData( $key, $value ) {
00574         if ( $value === null ) {
00575             unset( $this->mExtensionData[$key] );
00576         } else {
00577             $this->mExtensionData[$key] = $value;
00578         }
00579     }
00580 
00592     public function getExtensionData( $key ) {
00593         if ( isset( $this->mExtensionData[$key] ) ) {
00594             return $this->mExtensionData[$key];
00595         }
00596 
00597         return null;
00598     }
00599 
00600     private static function getTimes( $clock = null ) {
00601         $ret = array();
00602         if ( !$clock || $clock === 'wall' ) {
00603             $ret['wall'] = microtime( true );
00604         }
00605         if ( ( !$clock || $clock === 'cpu' ) && function_exists( 'getrusage' ) ) {
00606             $ru = getrusage();
00607             $ret['cpu'] = $ru['ru_utime.tv_sec'] + $ru['ru_utime.tv_usec'] / 1e6;
00608             $ret['cpu'] += $ru['ru_stime.tv_sec'] + $ru['ru_stime.tv_usec'] / 1e6;
00609         }
00610         return $ret;
00611     }
00612 
00617     function resetParseStartTime() {
00618         $this->mParseStartTime = self::getTimes();
00619     }
00620 
00632     function getTimeSinceStart( $clock ) {
00633         if ( !isset( $this->mParseStartTime[$clock] ) ) {
00634             return null;
00635         }
00636 
00637         $end = self::getTimes( $clock );
00638         return $end[$clock] - $this->mParseStartTime[$clock];
00639     }
00640 
00660     function setLimitReportData( $key, $value ) {
00661         $this->mLimitReportData[$key] = $value;
00662     }
00663 
00671     public function preventClickjacking( $flag = null ) {
00672         return wfSetVar( $this->mPreventClickjacking, $flag );
00673     }
00674 
00678     function __sleep() {
00679         return array_diff(
00680             array_keys( get_object_vars( $this ) ),
00681             array( 'mSecondaryDataUpdates', 'mParseStartTime' )
00682         );
00683     }
00684 }