Documentation tweaks to help documentation systems (Doxygen + PHPDocumentor)
[lhc/web/wiklou.git] / includes / Parser.php
index 68470c8..4b85c1a 100644 (file)
@@ -2,8 +2,7 @@
 /**
  * File for Parser and related classes
  *
- * @package MediaWiki
- * @subpackage Parser
+ * @addtogroup Parser
  */
 
 /**
@@ -57,9 +56,10 @@ define( 'MW_COLON_STATE_COMMENTDASH', 6 );
 define( 'MW_COLON_STATE_COMMENTDASHDASH', 7 );
 
 /**
- * PHP Parser
- *
- * Processes wiki markup
+ * PHP Parser - Processes wiki markup (which uses a more user-friendly 
+ * syntax, such as "[[link]]" for making links), and provides a one-way
+ * transformation of that wiki markup it into XHTML output / markup
+ * (which in turn the browser understands, and can display).
  *
  * <pre>
  * There are four main entry points into the Parser class:
@@ -86,10 +86,10 @@ define( 'MW_COLON_STATE_COMMENTDASHDASH', 7 );
  *  * only within ParserOptions
  * </pre>
  *
- * @package MediaWiki
  */
 class Parser
 {
+       const VERSION = MW_PARSER_VERSION;
        /**#@+
         * @private
         */
@@ -723,7 +723,7 @@ class Parser
                $descriptorspec = array(
                        0 => array('pipe', 'r'),
                        1 => array('pipe', 'w'),
-                       2 => array('file', '/dev/null', 'a')
+                       2 => array('file', '/dev/null', 'a')  // FIXME: this line in UNIX-specific, it generates a warning on Windows, because /dev/null is not a valid Windows file.
                );
                $pipes = array();
                $process = proc_open("$wgTidyBin -config $wgTidyConf $wgTidyOpts$opts", $descriptorspec, $pipes);
@@ -1001,6 +1001,7 @@ class Parser
                $text = Sanitizer::removeHTMLtags( $text, array( &$this, 'attributeStripCallback' ) );
 
                $text = $this->replaceVariables( $text, $args );
+               wfRunHooks( 'InternalParseBeforeLinks', array( &$this, &$text ) );
 
                // Tables need to come after variable replacement for things to work
                // properly; putting them before other transformations should keep
@@ -1085,7 +1086,7 @@ class Parser
                        }
 
                        $url = wfMsg( $urlmsg, $id);
-                       $sk =& $this->mOptions->getSkin();
+                       $sk = $this->mOptions->getSkin();
                        $la = $sk->getExternalLinkAttributes( $url, $keyword.$id );
                        $text = "<a href=\"{$url}\"{$la}>{$keyword} {$id}</a>";
                }
@@ -1286,7 +1287,8 @@ class Parser
                                $output .= '</i>';
                        if ($state == 'bi')
                                $output .= '</b>';
-                       if ($state == 'both')
+                       # There might be lonely ''''', so make sure we have a buffer
+                       if ($state == 'both' && $buffer)
                                $output .= '<b><i>'.$buffer.'</i></b>';
                        return $output;
                }
@@ -1305,7 +1307,7 @@ class Parser
                $fname = 'Parser::replaceExternalLinks';
                wfProfileIn( $fname );
 
-               $sk =& $this->mOptions->getSkin();
+               $sk = $this->mOptions->getSkin();
 
                $bits = preg_split( EXT_LINK_BRACKETED, $text, -1, PREG_SPLIT_DELIM_CAPTURE );
 
@@ -1394,7 +1396,7 @@ class Parser
                $s = array_shift( $bits );
                $i = 0;
 
-               $sk =& $this->mOptions->getSkin();
+               $sk = $this->mOptions->getSkin();
 
                while ( $i < count( $bits ) ){
                        $protocol = $bits[$i++];
@@ -1467,7 +1469,7 @@ class Parser
         * @param string
         * @return string
         * @static
-        * @fixme This can merge genuinely required bits in the path or query string,
+        * @todo  This can merge genuinely required bits in the path or query string,
         *        breaking legit URLs. A proper fix would treat the various parts of
         *        the URL differently; as a workaround, just use the output for
         *        statistical records, not for actual linking/output.
@@ -1502,7 +1504,7 @@ class Parser
         * @private
         */
        function maybeMakeExternalImage( $url ) {
-               $sk =& $this->mOptions->getSkin();
+               $sk = $this->mOptions->getSkin();
                $imagesfrom = $this->mOptions->getAllowExternalImagesFrom();
                $imagesexception = !empty($imagesfrom);
                $text = false;
@@ -1532,7 +1534,7 @@ class Parser
                # the % is needed to support urlencoded titles as well
                if ( !$tc ) { $tc = Title::legalChars() . '#%'; }
 
-               $sk =& $this->mOptions->getSkin();
+               $sk = $this->mOptions->getSkin();
 
                #split the entire text string on occurences of [[
                $a = explode( '[[', ' ' . $s );
@@ -1625,7 +1627,7 @@ class Parser
                                $might_be_img = true;
                                $text = $m[2];
                                if ( strpos( $m[1], '%' ) !== false ) {
-                                      $m[1] = urldecode($m[1]);
+                                       $m[1] = urldecode($m[1]);
                                }
                                $trail = "";
                        } else { # Invalid form; output directly
@@ -1722,8 +1724,8 @@ class Parser
                                wfProfileIn( "$fname-interwiki" );
                                if( $iw && $this->mOptions->getInterwikiMagic() && $nottalk && $wgContLang->getLanguageName( $iw ) ) {
                                        $this->mOutput->addLanguageLink( $nt->getFullText() );
-                                       $s = rtrim($s . "\n");
-                                       $s .= trim($prefix . $trail, "\n") == '' ? '': $prefix . $trail;
+                                       $s = rtrim($s . $prefix);
+                                       $s .= trim($trail, "\n") == '' ? '': $prefix . $trail;
                                        wfProfileOut( "$fname-interwiki" );
                                        continue;
                                }
@@ -1862,7 +1864,7 @@ class Parser
         */
        function makeKnownLinkHolder( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
                list( $inside, $trail ) = Linker::splitTrail( $trail );
-               $sk =& $this->mOptions->getSkin();
+               $sk = $this->mOptions->getSkin();
                $link = $sk->makeKnownLinkObj( $nt, $text, $query, $inside, $prefix );
                return $this->armorLinks( $link ) . $trail;
        }
@@ -1923,9 +1925,9 @@ class Parser
                        # Look at the first character
                        if( $target != '' && $target{0} == '/' ) {
                                # / at end means we don't want the slash to be shown
-                               if( substr( $target, -1, 1 ) == '/' ) {
-                                       $target = substr( $target, 1, -1 );
-                                       $noslash = $target;
+                               $trailingSlashes = preg_match_all( '%(/+)$%', $target, $m );
+                               if( $trailingSlashes ) {
+                                       $noslash = $target = substr( $target, 1, -strlen($m[0][0]) );
                                } else {
                                        $noslash = substr( $target, 1 );
                                }
@@ -2888,7 +2890,7 @@ class Parser
         * @private
         */
        function braceSubstitution( $piece ) {
-               global $wgContLang, $wgLang, $wgAllowDisplayTitle;
+               global $wgContLang, $wgLang, $wgAllowDisplayTitle, $wgNonincludableNamespaces;
                $fname = __METHOD__ /*. '-L' . count( $this->mArgStack )*/;
                wfProfileIn( $fname );
                wfProfileIn( __METHOD__.'-setup' );
@@ -3031,6 +3033,19 @@ class Parser
                        } else {
                                # set $text to cached message.
                                $text = $linestart . $this->mTemplates[$piece['title']];
+                               #treat title for cached page the same as others
+                               $ns = NS_TEMPLATE;
+                               $subpage = '';
+                               $part1 = $this->maybeDoSubpageLink( $part1, $subpage );
+                               if ($subpage !== '') {
+                                 $ns = $this->mTitle->getNamespace();
+                               }
+                               $title = Title::newFromText( $part1, $ns );
+                               //used by include size checking
+                               $titleText = $title->getPrefixedText();
+                               //used by edit section links
+                               $replaceHeadings = true;
+                               
                        }
                }
 
@@ -3066,6 +3081,9 @@ class Parser
                                                        $isHTML = true;
                                                        $this->disableCache();
                                                }
+                                       } else if ( $wgNonincludableNamespaces && in_array( $title->getNamespace(), $wgNonincludableNamespaces ) ) {
+                                               $found = false; //access denied
+                                               wfDebug( "$fname: template inclusion denied for " . $title->getPrefixedDBkey() );
                                        } else {
                                                $articleContent = $this->fetchTemplate( $title );
                                                if ( $articleContent !== false ) {
@@ -3279,7 +3297,7 @@ class Parser
 
        function fetchScaryTemplateMaybeFromCache($url) {
                global $wgTranscludeCacheExpiry;
-               $dbr =& wfGetDB(DB_SLAVE);
+               $dbr = wfGetDB(DB_SLAVE);
                $obj = $dbr->selectRow('transcache', array('tc_time', 'tc_contents'),
                                array('tc_url' => $url));
                if ($obj) {
@@ -3294,7 +3312,7 @@ class Parser
                if (!$text)
                        return wfMsg('scarytranscludefailed', $url);
 
-               $dbw =& wfGetDB(DB_MASTER);
+               $dbw = wfGetDB(DB_MASTER);
                $dbw->replace('transcache', array('tc_url'), array(
                        'tc_url' => $url,
                        'tc_time' => time(),
@@ -3437,7 +3455,7 @@ class Parser
                }
 
                # We need this to perform operations on the HTML
-               $sk =& $this->mOptions->getSkin();
+               $sk = $this->mOptions->getSkin();
 
                # headline counter
                $headlineCount = 0;
@@ -3962,12 +3980,12 @@ class Parser
 
                $pdbks = array();
                $colours = array();
-               $sk =& $this->mOptions->getSkin();
+               $sk = $this->mOptions->getSkin();
                $linkCache =& LinkCache::singleton();
 
                if ( !empty( $this->mLinkHolders['namespaces'] ) ) {
                        wfProfileIn( $fname.'-check' );
-                       $dbr =& wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_SLAVE );
                        $page = $dbr->tableName( 'page' );
                        $threshold = $wgUser->getOption('stubthreshold');
 
@@ -4299,6 +4317,7 @@ class Parser
         */
        function renderImageGallery( $text, $params ) {
                $ig = new ImageGallery();
+               $ig->setContextTitle( $this->mTitle );
                $ig->setShowBytes( false );
                $ig->setShowFilename( false );
                $ig->setParsing();
@@ -4310,6 +4329,15 @@ class Parser
                        $caption = $this->replaceInternalLinks( $caption );
                        $ig->setCaptionHtml( $caption );
                }
+               if( isset( $params['perrow'] ) ) {
+                       $ig->setPerRow( $params['perrow'] );
+               }
+               if( isset( $params['widths'] ) ) {
+                       $ig->setWidths( $params['widths'] );
+               }
+               if( isset( $params['heights'] ) ) {
+                       $ig->setHeights( $params['heights'] );
+               }
 
                $lines = explode( "\n", $text );
                foreach ( $lines as $line ) {
@@ -4357,8 +4385,6 @@ class Parser
        function makeImage( $nt, $options ) {
                global $wgUseImageResize, $wgDjvuRenderer;
 
-               $align = '';
-
                # Check if the options text is of the form "options|alt text"
                # Options are:
                #  * thumbnail          make a thumbnail with enlarge-icon and caption, alignment depends on lang
@@ -4368,16 +4394,26 @@ class Parser
                #  * ___px              scale to ___ pixels width, no aligning. e.g. use in taxobox
                #  * center             center the image
                #  * framed             Keep original image size, no magnify-button.
-
-               $part = explode( '|', $options);
-
+               # vertical-align values (no % or length right now):
+               #  * baseline
+               #  * sub
+               #  * super
+               #  * top
+               #  * text-top
+               #  * middle
+               #  * bottom
+               #  * text-bottom
+
+               $part = array_map( 'trim', explode( '|', $options) );
+
+               $mwAlign = array();
+               $alignments = array( 'left', 'right', 'center', 'none', 'baseline', 'sub', 'super', 'top', 'text-top', 'middle', 'bottom', 'text-bottom' );
+               foreach ( $alignments as $alignment ) {
+                       $mwAlign[$alignment] =& MagicWord::get( 'img_'.$alignment );
+               }
                $mwThumb  =& MagicWord::get( 'img_thumbnail' );
                $mwManualThumb =& MagicWord::get( 'img_manualthumb' );
-               $mwLeft   =& MagicWord::get( 'img_left' );
-               $mwRight  =& MagicWord::get( 'img_right' );
-               $mwNone   =& MagicWord::get( 'img_none' );
                $mwWidth  =& MagicWord::get( 'img_width' );
-               $mwCenter =& MagicWord::get( 'img_center' );
                $mwFramed =& MagicWord::get( 'img_framed' );
                $mwPage   =& MagicWord::get( 'img_page' );
                $caption = '';
@@ -4385,6 +4421,7 @@ class Parser
                $width = $height = $framed = $thumb = false;
                $page = null;
                $manual_thumb = '' ;
+               $align = $valign = '';
 
                foreach( $part as $val ) {
                        if ( $wgUseImageResize && ! is_null( $mwThumb->matchVariableStartToEnd($val) ) ) {
@@ -4393,36 +4430,37 @@ class Parser
                                # use manually specified thumbnail
                                $thumb=true;
                                $manual_thumb = $match;
-                       } elseif ( ! is_null( $mwRight->matchVariableStartToEnd($val) ) ) {
-                               # remember to set an alignment, don't render immediately
-                               $align = 'right';
-                       } elseif ( ! is_null( $mwLeft->matchVariableStartToEnd($val) ) ) {
-                               # remember to set an alignment, don't render immediately
-                               $align = 'left';
-                       } elseif ( ! is_null( $mwCenter->matchVariableStartToEnd($val) ) ) {
-                               # remember to set an alignment, don't render immediately
-                               $align = 'center';
-                       } elseif ( ! is_null( $mwNone->matchVariableStartToEnd($val) ) ) {
-                               # remember to set an alignment, don't render immediately
-                               $align = 'none';
-                       } elseif ( isset( $wgDjvuRenderer ) && $wgDjvuRenderer
-                                  && ! is_null( $match = $mwPage->matchVariableStartToEnd($val) ) ) {
-                               # Select a page in a multipage document
-                               $page = $match;
-                       } elseif ( $wgUseImageResize && !$width && ! is_null( $match = $mwWidth->matchVariableStartToEnd($val) ) ) {
-                               wfDebug( "img_width match: $match\n" );
-                               # $match is the image width in pixels
-                               $m = array();
-                               if ( preg_match( '/^([0-9]*)x([0-9]*)$/', $match, $m ) ) {
-                                       $width = intval( $m[1] );
-                                       $height = intval( $m[2] );
+                       } else {
+                               foreach( $alignments as $alignment ) {
+                                       if ( ! is_null( $mwAlign[$alignment]->matchVariableStartToEnd($val) ) ) {
+                                               switch ( $alignment ) {
+                                                       case 'left': case 'right': case 'center': case 'none':
+                                                               $align = $alignment; break;
+                                                       default:
+                                                               $valign = $alignment;
+                                               }
+                                               continue 2;
+                                       }
+                               }
+                               if ( isset( $wgDjvuRenderer ) && $wgDjvuRenderer
+                               && ! is_null( $match = $mwPage->matchVariableStartToEnd($val) ) ) {
+                                       # Select a page in a multipage document
+                                       $page = $match;
+                               } elseif ( $wgUseImageResize && !$width && ! is_null( $match = $mwWidth->matchVariableStartToEnd($val) ) ) {
+                                       wfDebug( "img_width match: $match\n" );
+                                       # $match is the image width in pixels
+                                       $m = array();
+                                       if ( preg_match( '/^([0-9]*)x([0-9]*)$/', $match, $m ) ) {
+                                               $width = intval( $m[1] );
+                                               $height = intval( $m[2] );
+                                       } else {
+                                               $width = intval($match);
+                                       }
+                               } elseif ( ! is_null( $mwFramed->matchVariableStartToEnd($val) ) ) {
+                                       $framed=true;
                                } else {
-                                       $width = intval($match);
+                                       $caption = $val;
                                }
-                       } elseif ( ! is_null( $mwFramed->matchVariableStartToEnd($val) ) ) {
-                               $framed=true;
-                       } else {
-                               $caption = $val;
                        }
                }
                # Strip bad stuff out of the alt text
@@ -4435,8 +4473,8 @@ class Parser
                $alt = Sanitizer::stripAllTags( $alt );
 
                # Linker does the rest
-               $sk =& $this->mOptions->getSkin();
-               return $sk->makeImageLinkObj( $nt, $caption, $alt, $align, $width, $height, $framed, $thumb, $manual_thumb, $page );
+               $sk = $this->mOptions->getSkin();
+               return $sk->makeImageLinkObj( $nt, $caption, $alt, $align, $width, $height, $framed, $thumb, $manual_thumb, $page, $valign );
        }
 
        /**
@@ -4514,24 +4552,6 @@ class Parser
                $uniq = preg_quote( $this->uniqPrefix(), '/' );
                $comment = "(?:$uniq-!--.*?QINU)";
                $secs = preg_split(
-               /*
-                       "/
-                       ^(
-                       (?:$comment|<\/?noinclude>)* # Initial comments will be stripped
-                       (?:
-                               (=+) # Should this be limited to 6?
-                               .+?  # Section title...
-                               \\2  # Ending = count must match start
-                       |
-                               ^
-                               <h([1-6])\b.*?>
-                               .*?
-                               <\/h\\3\s*>
-                       )
-                       (?:$comment|<\/?noinclude>|\s+)* # Trailing whitespace ok
-                       )$
-                       /mix",
-               */
                        "/
                        (
                                ^
@@ -4555,7 +4575,8 @@ class Parser
                                // "Section 0" returns the content before any other section.
                                $rv = $secs[0];
                        } else {
-                               $rv = "";
+                               //track missing section, will replace if found.
+                               $rv = $newtext;
                        }
                } elseif( $mode == "replace" ) {
                        if( $section == 0 ) {
@@ -4610,8 +4631,10 @@ class Parser
                                }
                        }
                }
-               # reinsert stripped tags
-               $rv = trim( $stripState->unstripBoth( $rv ) );
+               if (is_string($rv))
+                       # reinsert stripped tags
+                       $rv = trim( $stripState->unstripBoth( $rv ) );
+
                return $rv;
        }
 
@@ -4624,13 +4647,14 @@ class Parser
         *
         * @param $text String: text to look in
         * @param $section Integer: section number
+        * @param $deftext: default to return if section is not found
         * @return string text of the requested section
         */
-       function getSection( $text, $section ) {
-               return $this->extractSections( $text, $section, "get" );
+       public function getSection( $text, $section, $deftext='' ) {
+               return $this->extractSections( $text, $section, "get", $deftext );
        }
 
-       function replaceSection( $oldtext, $section, $text ) {
+       public function replaceSection( $oldtext, $section, $text ) {
                return $this->extractSections( $oldtext, $section, "replace", $text );
        }
 
@@ -4642,7 +4666,7 @@ class Parser
                if ( is_null( $this->mRevisionTimestamp ) ) {
                        wfProfileIn( __METHOD__ );
                        global $wgContLang;
-                       $dbr =& wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_SLAVE );
                        $timestamp = $dbr->selectField( 'revision', 'rev_timestamp',
                                        array( 'rev_id' => $this->mRevisionId ), __METHOD__ );
 
@@ -4692,6 +4716,9 @@ class Parser
 
 }
 
+/**
+ * @todo document, briefly.
+ */
 class OnlyIncludeReplacer {
        var $output = '';
 
@@ -4704,6 +4731,9 @@ class OnlyIncludeReplacer {
        }
 }
 
+/**
+ * @todo document, briefly.
+ */
 class StripState {
        var $general, $nowiki;