Fix for bug 13770, second attempt. Tested with a conflicting install of both dom...
[lhc/web/wiklou.git] / includes / parser / Parser.php
index da0d68d..346be86 100644 (file)
@@ -92,7 +92,7 @@ class Parser
        # Persistent:
        var $mTagHooks, $mTransparentTagHooks, $mFunctionHooks, $mFunctionSynonyms, $mVariables,
                $mImageParams, $mImageParamsMagicArray, $mStripList, $mMarkerIndex, $mPreprocessor,
-               $mExtLinkBracketedRegex, $mDefaultStripList, $mVarCache, $mConf;
+               $mExtLinkBracketedRegex, $mUrlProtocols, $mDefaultStripList, $mVarCache, $mConf;
 
 
        # Cleared with clearState():
@@ -128,11 +128,16 @@ class Parser
                $this->mFunctionHooks = array();
                $this->mFunctionSynonyms = array( 0 => array(), 1 => array() );
                $this->mDefaultStripList = $this->mStripList = array( 'nowiki', 'gallery' );
+               $this->mUrlProtocols = wfUrlProtocols();
                $this->mExtLinkBracketedRegex = '/\[(\b(' . wfUrlProtocols() . ')'.
                        '[^][<>"\\x00-\\x20\\x7F]+) *([^\]\\x0a\\x0d]*?)\]/S';
                $this->mVarCache = array();
                if ( isset( $conf['preprocessorClass'] ) ) {
                        $this->mPreprocessorClass = $conf['preprocessorClass'];
+               } elseif ( extension_loaded( 'domxml' ) ) {
+                       // PECL extension that conflicts with the core DOM extension (bug 13770)
+                       wfDebug( "Warning: you have the obsolete domxml extension for PHP. Please remove it!\n" );
+                       $this->mPreprocessorClass = 'Preprocessor_Hash';
                } elseif ( extension_loaded( 'dom' ) ) {
                        $this->mPreprocessorClass = 'Preprocessor_DOM';
                } else {
@@ -142,6 +147,18 @@ class Parser
                $this->mFirstCall = true;
        }
 
+       /**
+        * Reduce memory usage to reduce the impact of circular references
+        */
+       function __destruct() {
+               if ( isset( $this->mLinkHolders ) ) {
+                       $this->mLinkHolders->__destruct();
+               }
+               foreach ( $this as $name => $value ) {
+                       unset( $this->$name );
+               }
+       }
+
        /**
         * Do various kinds of initialisation on the first call of the parser
         */
@@ -541,7 +558,7 @@ class Parser
                                $text = $inside;
                                $tail = null;
                        } else {
-                               if( $element == '!--' ) {
+                               if( $element === '!--' ) {
                                        $end = '/(-->)/';
                                } else {
                                        $end = "/(<\\/$element\\s*>)/i";
@@ -763,7 +780,7 @@ class Parser
                        $line = trim( $outLine );
 
                        if( $line == '' ) { // empty line, go to next line
-                               $out .= "\n";
+                               $out .= $outLine."\n";
                                continue;
                        }
                        $first_character = $line[0];
@@ -786,7 +803,7 @@ class Parser
                                // Don't do any of the following
                                $out .= $outLine."\n";
                                continue;
-                       } else if ( substr ( $line , 0 , 2 ) == '|}' ) {
+                       } else if ( substr ( $line , 0 , 2 ) === '|}' ) {
                                // We are ending a table
                                $line = '</table>' . substr ( $line , 2 );
                                $last_tag = array_pop ( $last_tag_history );
@@ -804,7 +821,7 @@ class Parser
                                }
                                array_pop ( $tr_attributes );
                                $outLine = $line . str_repeat( '</dd></dl>' , $indent_level );
-                       } else if ( substr ( $line , 0 , 2 ) == '|-' ) {
+                       } else if ( substr ( $line , 0 , 2 ) === '|-' ) {
                                // Now we have a table row
                                $line = preg_replace( '#^\|-+#', '', $line );
 
@@ -832,16 +849,16 @@ class Parser
                                array_push ( $td_history , false );
                                array_push ( $last_tag_history , '' );
                        }
-                       else if ( $first_character == '|' || $first_character == '!' || substr ( $line , 0 , 2 )  == '|+' ) {
+                       else if ( $first_character === '|' || $first_character === '!' || substr ( $line , 0 , 2 )  === '|+' ) {
                                // This might be cell elements, td, th or captions
-                               if ( substr ( $line , 0 , 2 ) == '|+' ) {
+                               if ( substr ( $line , 0 , 2 ) === '|+' ) {
                                        $first_character = '+';
                                        $line = substr ( $line , 1 );
                                }
 
                                $line = substr ( $line , 1 );
 
-                               if ( $first_character == '!' ) {
+                               if ( $first_character === '!' ) {
                                        $line = str_replace ( '!!' , '||' , $line );
                                }
 
@@ -857,7 +874,7 @@ class Parser
                                foreach ( $cells as $cell )
                                {
                                        $previous = '';
-                                       if ( $first_character != '+' )
+                                       if ( $first_character !== '+' )
                                        {
                                                $tr_after = array_pop ( $tr_attributes );
                                                if ( !array_pop ( $tr_history ) ) {
@@ -875,11 +892,11 @@ class Parser
                                                $previous = "</{$last_tag}>{$previous}";
                                        }
 
-                                       if ( $first_character == '|' ) {
+                                       if ( $first_character === '|' ) {
                                                $last_tag = 'td';
-                                       } else if ( $first_character == '!' ) {
+                                       } else if ( $first_character === '!' ) {
                                                $last_tag = 'th';
-                                       } else if ( $first_character == '+' ) {
+                                       } else if ( $first_character === '+' ) {
                                                $last_tag = 'caption';
                                        } else {
                                                $last_tag = '';
@@ -926,12 +943,12 @@ class Parser
                }
 
                // Remove trailing line-ending (b/c)
-               if ( substr( $out, -1 ) == "\n" ) {
+               if ( substr( $out, -1 ) === "\n" ) {
                        $out = substr( $out, 0, -1 );
                }
 
                // special case: don't return empty table
-               if( $out == "<table>\n<tr><td></td></tr>\n</table>" ) {
+               if( $out === "<table>\n<tr><td></td></tr>\n</table>" ) {
                        $out = '';
                }
 
@@ -992,63 +1009,126 @@ class Parser
        /**
         * Replace special strings like "ISBN xxx" and "RFC xxx" with
         * magic external links.
-        *
+        * 
+        * DML
         * @private
         */
        function doMagicLinks( $text ) {
                wfProfileIn( __METHOD__ );
+               $prots = $this->mUrlProtocols;
+               $urlChar = self::EXT_LINK_URL_CLASS;
                $text = preg_replace_callback(
                        '!(?:                           # Start cases
-                           <a.*?</a> |                 # Skip link text
-                           <.*?> |                     # Skip stuff inside HTML elements
-                           (?:RFC|PMID)\s+([0-9]+) |   # RFC or PMID, capture number as m[1]
-                           ISBN\s+(\b                  # ISBN, capture number as m[2]
-                                     (?: 97[89] [\ \-]? )?   # optional 13-digit ISBN prefix
-                                     (?: [0-9]  [\ \-]? ){9} # 9 digits with opt. delimiters
-                                     [0-9Xx]                 # check digit
-                                   \b)
+                               (<a.*?</a>) |               # m[1]: Skip link text 
+                               (<.*?>) |                   # m[2]: Skip stuff inside HTML elements' . "
+                               (\\b(?:$prots)$urlChar+) |  # m[3]: Free external links" . '
+                               (?:RFC|PMID)\s+([0-9]+) |   # m[4]: RFC or PMID, capture number
+                               ISBN\s+(\b                  # m[5]: ISBN, capture number
+                                   (?: 97[89] [\ \-]? )?   # optional 13-digit ISBN prefix
+                                   (?: [0-9]  [\ \-]? ){9} # 9 digits with opt. delimiters
+                                   [0-9Xx]                 # check digit
+                                   \b)
                        )!x', array( &$this, 'magicLinkCallback' ), $text );
                wfProfileOut( __METHOD__ );
                return $text;
        }
 
        function magicLinkCallback( $m ) {
-               if ( substr( $m[0], 0, 1 ) == '<' ) {
+               if ( isset( $m[1] ) && strval( $m[1] ) !== '' ) {
+                       # Skip anchor
+                       return $m[0];
+               } elseif ( isset( $m[2] ) && strval( $m[2] ) !== '' ) {
                        # Skip HTML element
                        return $m[0];
-               } elseif ( substr( $m[0], 0, 4 ) == 'ISBN' ) {
-                       $isbn = $m[2];
-                       $num = strtr( $isbn, array(
-                               '-' => '',
-                               ' ' => '',
-                               'x' => 'X',
-                       ));
-                       $titleObj = SpecialPage::getTitleFor( 'Booksources', $num );
-                       $text = '<a href="' .
-                               $titleObj->escapeLocalUrl() .
-                               "\" class=\"internal\">ISBN $isbn</a>";
-               } else {
-                       if ( substr( $m[0], 0, 3 ) == 'RFC' ) {
+               } elseif ( isset( $m[3] ) && strval( $m[3] ) !== '' ) {
+                       # Free external link
+                       return $this->makeFreeExternalLink( $m[0] );
+               } elseif ( isset( $m[4] ) && strval( $m[4] ) !== '' ) {
+                       # RFC or PMID
+                       if ( substr( $m[0], 0, 3 ) === 'RFC' ) {
                                $keyword = 'RFC';
                                $urlmsg = 'rfcurl';
-                               $id = $m[1];
-                       } elseif ( substr( $m[0], 0, 4 ) == 'PMID' ) {
+                               $id = $m[4];
+                       } elseif ( substr( $m[0], 0, 4 ) === 'PMID' ) {
                                $keyword = 'PMID';
                                $urlmsg = 'pubmedurl';
-                               $id = $m[1];
+                               $id = $m[4];
                        } else {
                                throw new MWException( __METHOD__.': unrecognised match type "' .
                                        substr($m[0], 0, 20 ) . '"' );
                        }
-
                        $url = wfMsg( $urlmsg, $id);
                        $sk = $this->mOptions->getSkin();
                        $la = $sk->getExternalLinkAttributes( $url, $keyword.$id );
-                       $text = "<a href=\"{$url}\"{$la}>{$keyword} {$id}</a>";
+                       return "<a href=\"{$url}\"{$la}>{$keyword} {$id}</a>";
+               } elseif ( isset( $m[5] ) && strval( $m[5] ) !== '' ) {
+                       # ISBN
+                       $isbn = $m[5];
+                       $num = strtr( $isbn, array(
+                               '-' => '',
+                               ' ' => '',
+                               'x' => 'X',
+                       ));
+                       $titleObj = SpecialPage::getTitleFor( 'Booksources', $num );
+                       return'<a href="' .
+                               $titleObj->escapeLocalUrl() .
+                               "\" class=\"internal\">ISBN $isbn</a>";
+               } else {
+                       return $m[0];
                }
-               return $text;
        }
 
+       /**
+        * Make a free external link, given a user-supplied URL
+        * @return HTML
+        * @private
+        */
+       function makeFreeExternalLink( $url ) {
+               global $wgContLang;
+               wfProfileIn( __METHOD__ );
+
+               $sk = $this->mOptions->getSkin();
+               $trail = '';
+
+               # The characters '<' and '>' (which were escaped by
+               # removeHTMLtags()) should not be included in
+               # URLs, per RFC 2396.
+               $m2 = array();
+               if (preg_match('/&(lt|gt);/', $url, $m2, PREG_OFFSET_CAPTURE)) {
+                       $trail = substr($url, $m2[0][1]) . $trail;
+                       $url = substr($url, 0, $m2[0][1]);
+               }
+
+               # Move trailing punctuation to $trail
+               $sep = ',;\.:!?';
+               # If there is no left bracket, then consider right brackets fair game too
+               if ( strpos( $url, '(' ) === false ) {
+                       $sep .= ')';
+               }
+
+               $numSepChars = strspn( strrev( $url ), $sep );
+               if ( $numSepChars ) {
+                       $trail = substr( $url, -$numSepChars ) . $trail;
+                       $url = substr( $url, 0, -$numSepChars );
+               }
+
+               $url = Sanitizer::cleanUrl( $url );
+
+               # Is this an external image?
+               $text = $this->maybeMakeExternalImage( $url );
+               if ( $text === false ) {
+                       # Not an image, make a link
+                       $text = $sk->makeExternalLink( $url, $wgContLang->markNoConversion($url), true, 'free', $this->mTitle->getNamespace() );
+                       # Register it in the output object...
+                       # Replace unnecessary URL escape codes with their equivalent characters
+                       $pasteurized = self::replaceUnusualEscapes( $url );
+                       $this->mOutput->addExternalLink( $pasteurized );
+               }
+               wfProfileOut( __METHOD__ );
+               return $text . $trail;
+       }
+
+
        /**
         * Parse headers and return html
         *
@@ -1140,9 +1220,9 @@ class Parser
                                        {
                                                $x1 = substr ($arr[$i-1], -1);
                                                $x2 = substr ($arr[$i-1], -2, 1);
-                                               if ($x1 == ' ') {
+                                               if ($x1 === ' ') {
                                                        if ($firstspace == -1) $firstspace = $i;
-                                               } else if ($x2 == ' ') {
+                                               } else if ($x2 === ' ') {
                                                        if ($firstsingleletterword == -1) $firstsingleletterword = $i;
                                                } else {
                                                        if ($firstmultiletterword == -1) $firstmultiletterword = $i;
@@ -1182,7 +1262,7 @@ class Parser
                        {
                                if (($i % 2) == 0)
                                {
-                                       if ($state == 'both')
+                                       if ($state === 'both')
                                                $buffer .= $r;
                                        else
                                                $output .= $r;
@@ -1191,41 +1271,41 @@ class Parser
                                {
                                        if (strlen ($r) == 2)
                                        {
-                                               if ($state == 'i')
+                                               if ($state === 'i')
                                                { $output .= '</i>'; $state = ''; }
-                                               else if ($state == 'bi')
+                                               else if ($state === 'bi')
                                                { $output .= '</i>'; $state = 'b'; }
-                                               else if ($state == 'ib')
+                                               else if ($state === 'ib')
                                                { $output .= '</b></i><b>'; $state = 'b'; }
-                                               else if ($state == 'both')
+                                               else if ($state === 'both')
                                                { $output .= '<b><i>'.$buffer.'</i>'; $state = 'b'; }
                                                else # $state can be 'b' or ''
                                                { $output .= '<i>'; $state .= 'i'; }
                                        }
                                        else if (strlen ($r) == 3)
                                        {
-                                               if ($state == 'b')
+                                               if ($state === 'b')
                                                { $output .= '</b>'; $state = ''; }
-                                               else if ($state == 'bi')
+                                               else if ($state === 'bi')
                                                { $output .= '</i></b><i>'; $state = 'i'; }
-                                               else if ($state == 'ib')
+                                               else if ($state === 'ib')
                                                { $output .= '</b>'; $state = 'i'; }
-                                               else if ($state == 'both')
+                                               else if ($state === 'both')
                                                { $output .= '<i><b>'.$buffer.'</b>'; $state = 'i'; }
                                                else # $state can be 'i' or ''
                                                { $output .= '<b>'; $state .= 'b'; }
                                        }
                                        else if (strlen ($r) == 5)
                                        {
-                                               if ($state == 'b')
+                                               if ($state === 'b')
                                                { $output .= '</b><i>'; $state = 'i'; }
-                                               else if ($state == 'i')
+                                               else if ($state === 'i')
                                                { $output .= '</i><b>'; $state = 'b'; }
-                                               else if ($state == 'bi')
+                                               else if ($state === 'bi')
                                                { $output .= '</i></b>'; $state = ''; }
-                                               else if ($state == 'ib')
+                                               else if ($state === 'ib')
                                                { $output .= '</b></i>'; $state = ''; }
-                                               else if ($state == 'both')
+                                               else if ($state === 'both')
                                                { $output .= '<i><b>'.$buffer.'</b></i>'; $state = ''; }
                                                else # ($state == '')
                                                { $buffer = ''; $state = 'both'; }
@@ -1234,21 +1314,21 @@ class Parser
                                $i++;
                        }
                        # Now close all remaining tags.  Notice that the order is important.
-                       if ($state == 'b' || $state == 'ib')
+                       if ($state === 'b' || $state === 'ib')
                                $output .= '</b>';
-                       if ($state == 'i' || $state == 'bi' || $state == 'ib')
+                       if ($state === 'i' || $state === 'bi' || $state === 'ib')
                                $output .= '</i>';
-                       if ($state == 'bi')
+                       if ($state === 'bi')
                                $output .= '</b>';
                        # There might be lonely ''''', so make sure we have a buffer
-                       if ($state == 'both' && $buffer)
+                       if ($state === 'both' && $buffer)
                                $output .= '<b><i>'.$buffer.'</i></b>';
                        return $output;
                }
        }
 
        /**
-        * Replace external links
+        * Replace external links (REL)
         *
         * Note: this is all very hackish and the order of execution matters a lot.
         * Make sure to run maintenance/parserTests.php if you change this code.
@@ -1262,8 +1342,7 @@ class Parser
                $sk = $this->mOptions->getSkin();
 
                $bits = preg_split( $this->mExtLinkBracketedRegex, $text, -1, PREG_SPLIT_DELIM_CAPTURE );
-
-               $s = $this->replaceFreeExternalLinks( array_shift( $bits ) );
+               $s = array_shift( $bits );
 
                $i = 0;
                while ( $i<count( $bits ) ) {
@@ -1291,7 +1370,7 @@ class Parser
                        $dtrail = '';
 
                        # Set linktype for CSS - if URL==text, link is essentially free
-                       $linktype = ($text == $url) ? 'free' : 'text';
+                       $linktype = ($text === $url) ? 'free' : 'text';
 
                        # No link text, e.g. [http://domain.tld/some.link]
                        if ( $text == '' ) {
@@ -1314,10 +1393,6 @@ class Parser
 
                        $url = Sanitizer::cleanUrl( $url );
 
-                       # Process the trail (i.e. everything after this link up until start of the next link),
-                       # replacing any non-bracketed links
-                       $trail = $this->replaceFreeExternalLinks( $trail );
-
                        # Use the encoded URL
                        # This means that users can paste URLs directly into the text
                        # Funny characters like &ouml; aren't valid in URLs anyway
@@ -1335,86 +1410,6 @@ class Parser
                return $s;
        }
 
-       /**
-        * Replace anything that looks like a URL with a link
-        * @private
-        */
-       function replaceFreeExternalLinks( $text ) {
-               global $wgContLang;
-               wfProfileIn( __METHOD__ );
-
-               $bits = preg_split( '/(\b(?:' . wfUrlProtocols() . '))/S', $text, -1, PREG_SPLIT_DELIM_CAPTURE );
-               $s = array_shift( $bits );
-               $i = 0;
-
-               $sk = $this->mOptions->getSkin();
-
-               while ( $i < count( $bits ) ){
-                       $protocol = $bits[$i++];
-                       $remainder = $bits[$i++];
-
-                       $m = array();
-                       if ( preg_match( '/^('.self::EXT_LINK_URL_CLASS.'+)(.*)$/s', $remainder, $m ) ) {
-                               # Found some characters after the protocol that look promising
-                               $url = $protocol . $m[1];
-                               $trail = $m[2];
-
-                               # special case: handle urls as url args:
-                               # http://www.example.com/foo?=http://www.example.com/bar
-                               if(strlen($trail) == 0 &&
-                                       isset($bits[$i]) &&
-                                       preg_match('/^'. wfUrlProtocols() . '$/S', $bits[$i]) &&
-                                       preg_match( '/^('.self::EXT_LINK_URL_CLASS.'+)(.*)$/s', $bits[$i + 1], $m ))
-                               {
-                                       # add protocol, arg
-                                       $url .= $bits[$i] . $m[1]; # protocol, url as arg to previous link
-                                       $i += 2;
-                                       $trail = $m[2];
-                               }
-
-                               # The characters '<' and '>' (which were escaped by
-                               # removeHTMLtags()) should not be included in
-                               # URLs, per RFC 2396.
-                               $m2 = array();
-                               if (preg_match('/&(lt|gt);/', $url, $m2, PREG_OFFSET_CAPTURE)) {
-                                       $trail = substr($url, $m2[0][1]) . $trail;
-                                       $url = substr($url, 0, $m2[0][1]);
-                               }
-
-                               # Move trailing punctuation to $trail
-                               $sep = ',;\.:!?';
-                               # If there is no left bracket, then consider right brackets fair game too
-                               if ( strpos( $url, '(' ) === false ) {
-                                       $sep .= ')';
-                               }
-
-                               $numSepChars = strspn( strrev( $url ), $sep );
-                               if ( $numSepChars ) {
-                                       $trail = substr( $url, -$numSepChars ) . $trail;
-                                       $url = substr( $url, 0, -$numSepChars );
-                               }
-
-                               $url = Sanitizer::cleanUrl( $url );
-
-                               # Is this an external image?
-                               $text = $this->maybeMakeExternalImage( $url );
-                               if ( $text === false ) {
-                                       # Not an image, make a link
-                                       $text = $sk->makeExternalLink( $url, $wgContLang->markNoConversion($url), true, 'free', $this->mTitle->getNamespace() );
-                                       # Register it in the output object...
-                                       # Replace unnecessary URL escape codes with their equivalent characters
-                                       $pasteurized = self::replaceUnusualEscapes( $url );
-                                       $this->mOutput->addExternalLink( $pasteurized );
-                               }
-                               $s .= $text . $trail;
-                       } else {
-                               $s .= $protocol . $remainder;
-                       }
-               }
-               wfProfileOut( __METHOD__ );
-               return $s;
-       }
-
        /**
         * Replace unusual URL escape codes with their equivalent characters
         * @param string
@@ -1451,7 +1446,7 @@ class Parser
 
        /**
         * make an image if it's allowed, either through the global
-        * option or through the exception
+        * option, through the exception, or through the on-wiki whitelist
         * @private
         */
        function maybeMakeExternalImage( $url ) {
@@ -1459,13 +1454,41 @@ class Parser
                $imagesfrom = $this->mOptions->getAllowExternalImagesFrom();
                $imagesexception = !empty($imagesfrom);
                $text = false;
+               # $imagesfrom could be either a single string or an array of strings, parse out the latter
+               if( $imagesexception && is_array( $imagesfrom ) ) {
+                       $imagematch = false;
+                       foreach( $imagesfrom as $match ) {
+                               if( strpos( $url, $match ) === 0 ) {
+                                       $imagematch = true;
+                                       break;
+                               }
+                       }
+               } elseif( $imagesexception ) {
+                       $imagematch = (strpos( $url, $imagesfrom ) === 0);
+               } else {
+                       $imagematch = false;
+               }
                if ( $this->mOptions->getAllowExternalImages()
-                    || ( $imagesexception && strpos( $url, $imagesfrom ) === 0 ) ) {
+                    || ( $imagesexception && $imagematch ) ) {
                        if ( preg_match( self::EXT_IMAGE_REGEX, $url ) ) {
                                # Image found
                                $text = $sk->makeExternalImage( $url );
                        }
                }
+               if( !$text && $this->mOptions->getEnableImageWhitelist()
+                        && preg_match( self::EXT_IMAGE_REGEX, $url ) ) {
+                       $whitelist = explode( "\n", wfMsgForContent( 'external_image_whitelist' ) );
+                       foreach( $whitelist as $entry ) {
+                               # Sanitize the regex fragment, make it case-insensitive, ignore blank entries/comments
+                               if( strpos( $entry, '#' ) === 0 || $entry === '' )
+                                       continue;
+                               if( preg_match( '/' . str_replace( '/', '\\/', $entry ) . '/i', $url ) ) {
+                                       # Image matches a whitelist entry
+                                       $text = $sk->makeExternalImage( $url );
+                                       break;
+                               }
+                       }
+               }
                return $text;
        }
 
@@ -1481,7 +1504,7 @@ class Parser
        }
 
        /**
-        * Process [[ ]] wikilinks
+        * Process [[ ]] wikilinks (RIL)
         * @return LinkHolderArray
         *
         * @private
@@ -1631,7 +1654,7 @@ class Parser
                                $link = $m[1];
                        }
 
-                       $noforce = (substr($m[1], 0, 1) != ':');
+                       $noforce = (substr($m[1], 0, 1) !== ':');
                        if (!$noforce) {
                                # Strip off leading ':'
                                $link = substr($link, 1);
@@ -1838,10 +1861,8 @@ class Parser
         * Insert a NOPARSE hacky thing into any inline links in a chunk that's
         * going to go through further parsing steps before inline URL expansion.
         *
-        * In particular this is important when using action=render, which causes
-        * full URLs to be included.
-        *
-        * Oh man I hate our multi-layer parser!
+        * Not needed quite as much as it used to be since free links are a bit
+        * more sensible these days. But bracketed links are still an issue.
         *
         * @param string more-or-less HTML
         * @return string less-or-more HTML with NOPARSE bits
@@ -1892,7 +1913,7 @@ class Parser
                        # bug 7425
                        $target = trim( $target );
                        # Look at the first character
-                       if( $target != '' && $target{0} == '/' ) {
+                       if( $target != '' && $target{0} === '/' ) {
                                # / at end means we don't want the slash to be shown
                                $m = array();
                                $trailingSlashes = preg_match_all( '%(/+)$%', $target, $m );
@@ -1919,7 +1940,7 @@ class Parser
                                        if( count( $exploded ) > $dotdotcount ) { # not allowed to go below top level page
                                                $ret = implode( '/', array_slice( $exploded, 0, -$dotdotcount ) );
                                                # / at the end means don't show full path
-                                               if( substr( $nodotdot, -1, 1 ) == '/' ) {
+                                               if( substr( $nodotdot, -1, 1 ) === '/' ) {
                                                        $nodotdot = substr( $nodotdot, 0, -1 );
                                                        if( '' === $text ) {
                                                                $text = $nodotdot . $suffix;
@@ -1971,10 +1992,10 @@ class Parser
        /* private */ function openList( $char ) {
                $result = $this->closeParagraph();
 
-               if ( '*' == $char ) { $result .= '<ul><li>'; }
-               else if ( '#' == $char ) { $result .= '<ol><li>'; }
-               else if ( ':' == $char ) { $result .= '<dl><dd>'; }
-               else if ( ';' == $char ) {
+               if ( '*' === $char ) { $result .= '<ul><li>'; }
+               else if ( '#' === $char ) { $result .= '<ol><li>'; }
+               else if ( ':' === $char ) { $result .= '<dl><dd>'; }
+               else if ( ';' === $char ) {
                        $result .= '<dl><dt>';
                        $this->mDTopen = true;
                }
@@ -1984,11 +2005,11 @@ class Parser
        }
 
        /* private */ function nextItem( $char ) {
-               if ( '*' == $char || '#' == $char ) { return '</li><li>'; }
-               else if ( ':' == $char || ';' == $char ) {
+               if ( '*' === $char || '#' === $char ) { return '</li><li>'; }
+               else if ( ':' === $char || ';' === $char ) {
                        $close = '</dd>';
                        if ( $this->mDTopen ) { $close = '</dt>'; }
-                       if ( ';' == $char ) {
+                       if ( ';' === $char ) {
                                $this->mDTopen = true;
                                return $close . '<dt>';
                        } else {
@@ -2000,9 +2021,9 @@ class Parser
        }
 
        /* private */ function closeList( $char ) {
-               if ( '*' == $char ) { $text = '</li></ul>'; }
-               else if ( '#' == $char ) { $text = '</li></ol>'; }
-               else if ( ':' == $char ) {
+               if ( '*' === $char ) { $text = '</li></ul>'; }
+               else if ( '#' === $char ) { $text = '</li></ol>'; }
+               else if ( ':' === $char ) {
                        if ( $this->mDTopen ) {
                                $this->mDTopen = false;
                                $text = '</dt></dl>';
@@ -2016,7 +2037,7 @@ class Parser
        /**#@-*/
 
        /**
-        * Make lists from lines starting with ':', '*', '#', etc.
+        * Make lists from lines starting with ':', '*', '#', etc. (DBL)
         *
         * @private
         * @return string the lists rendered as HTML
@@ -2068,7 +2089,7 @@ class Parser
                                $output .= $this->nextItem( substr( $prefix, -1 ) );
                                $paragraphStack = false;
 
-                               if ( substr( $prefix, -1 ) == ';') {
+                               if ( substr( $prefix, -1 ) === ';') {
                                        # The one nasty exception: definition lists work like this:
                                        # ; title : definition text
                                        # So we check for : in the remainder text to split up the
@@ -2095,7 +2116,7 @@ class Parser
                                        $char = substr( $prefix, $commonPrefixLength, 1 );
                                        $output .= $this->openList( $char );
 
-                                       if ( ';' == $char ) {
+                                       if ( ';' === $char ) {
                                                # FIXME: This is dupe of code above
                                                if ($this->findColonNoLinks($t, $term, $t2) !== false) {
                                                        $t = $t2;
@@ -2127,9 +2148,9 @@ class Parser
                                                $inBlockElem = true;
                                        }
                                } else if ( !$inBlockElem && !$this->mInPre ) {
-                                       if ( ' ' == $t{0} and ( $this->mLastSection == 'pre' or trim($t) != '' ) ) {
+                                       if ( ' ' == $t{0} and ( $this->mLastSection === 'pre' or trim($t) != '' ) ) {
                                                // pre
-                                               if ($this->mLastSection != 'pre') {
+                                               if ($this->mLastSection !== 'pre') {
                                                        $paragraphStack = false;
                                                        $output .= $this->closeParagraph().'<pre>';
                                                        $this->mLastSection = 'pre';
@@ -2143,7 +2164,7 @@ class Parser
                                                                $paragraphStack = false;
                                                                $this->mLastSection = 'p';
                                                        } else {
-                                                               if ($this->mLastSection != 'p' ) {
+                                                               if ($this->mLastSection !== 'p' ) {
                                                                        $output .= $this->closeParagraph();
                                                                        $this->mLastSection = '';
                                                                        $paragraphStack = '<p>';
@@ -2156,7 +2177,7 @@ class Parser
                                                                $output .= $paragraphStack;
                                                                $paragraphStack = false;
                                                                $this->mLastSection = 'p';
-                                                       } else if ($this->mLastSection != 'p') {
+                                                       } else if ($this->mLastSection !== 'p') {
                                                                $output .= $this->closeParagraph().'<p>';
                                                                $this->mLastSection = 'p';
                                                        }
@@ -2299,7 +2320,7 @@ class Parser
                                break;
                        case 3: // self::COLON_STATE_CLOSETAG:
                                // In a </tag>
-                               if( $c == ">" ) {
+                               if( $c === ">" ) {
                                        $stack--;
                                        if( $stack < 0 ) {
                                                wfDebug( __METHOD__.": Invalid input; too many close tags\n" );
@@ -2310,7 +2331,7 @@ class Parser
                                }
                                break;
                        case self::COLON_STATE_TAGSLASH:
-                               if( $c == ">" ) {
+                               if( $c === ">" ) {
                                        // Yes, a self-closed tag <blah/>
                                        $state = self::COLON_STATE_TEXT;
                                } else {
@@ -2319,19 +2340,19 @@ class Parser
                                }
                                break;
                        case 5: // self::COLON_STATE_COMMENT:
-                               if( $c == "-" ) {
+                               if( $c === "-" ) {
                                        $state = self::COLON_STATE_COMMENTDASH;
                                }
                                break;
                        case self::COLON_STATE_COMMENTDASH:
-                               if( $c == "-" ) {
+                               if( $c === "-" ) {
                                        $state = self::COLON_STATE_COMMENTDASHDASH;
                                } else {
                                        $state = self::COLON_STATE_COMMENT;
                                }
                                break;
                        case self::COLON_STATE_COMMENTDASHDASH:
-                               if( $c == ">" ) {
+                               if( $c === ">" ) {
                                        $state = self::COLON_STATE_TEXT;
                                } else {
                                        $state = self::COLON_STATE_COMMENT;
@@ -2881,7 +2902,7 @@ class Parser
                                $titleText = $title->getPrefixedText();
                                # Check for language variants if the template is not found
                                if($wgContLang->hasVariants() && $title->getArticleID() == 0){
-                                       $wgContLang->findVariantLink($part1, $title);
+                                       $wgContLang->findVariantLink( $part1, $title, true );
                                }
                                # Do infinite loop check
                                if ( !$frame->loopCheck( $title ) ) {
@@ -3291,7 +3312,7 @@ class Parser
                        }
                }
 
-               if ( $name == 'html' || $name == 'nowiki' ) {
+               if ( $name === 'html' || $name === 'nowiki' ) {
                        $this->mStripState->nowiki->setPair( $marker, $output );
                } else {
                        $this->mStripState->general->setPair( $marker, $output );
@@ -3623,7 +3644,7 @@ class Parser
                $i = 0;
 
                foreach( $blocks as $block ) {
-                       if( $showEditLink && $headlineCount > 0 && $i == 0 && $block != "\n" ) {
+                       if( $showEditLink && $headlineCount > 0 && $i == 0 && $block !== "\n" ) {
                                # This is the [edit] link that appears for the top block of text when
                                # section editing is enabled
 
@@ -3725,11 +3746,13 @@ class Parser
                $nc = '[ _0-9A-Za-z\x80-\xff-]'; # Namespaces can use non-ascii!
 
                $p1 = "/\[\[(:?$nc+:|:|)($tc+?)( \\($tc+\\))\\|]]/";            # [[ns:page (context)|]]
+               $p4 = "/\[\[(:?$nc+:|:|)($tc+?)(($tc+))\\|]]/";             # [[ns:page(context)|]]
                $p3 = "/\[\[(:?$nc+:|:|)($tc+?)( \\($tc+\\)|)(, $tc+|)\\|]]/";  # [[ns:page (context), context|]]
                $p2 = "/\[\[\\|($tc+)]]/";                                      # [[|page]]
 
                # try $p1 first, to turn "[[A, B (C)|]]" into "[[A, B (C)|A, B]]"
                $text = preg_replace( $p1, '[[\\1\\2\\3|\\2]]', $text );
+               $text = preg_replace( $p4, '[[\\1\\2\\3|\\2]]', $text );
                $text = preg_replace( $p3, '[[\\1\\2\\3\\4|\\2]]', $text );
 
                $t = $this->mTitle->getText();
@@ -4003,7 +4026,7 @@ class Parser
                                $syn = '#' . $syn;
                        }
                        # Remove trailing colon
-                       if ( substr( $syn, -1, 1 ) == ':' ) {
+                       if ( substr( $syn, -1, 1 ) === ':' ) {
                                $syn = substr( $syn, 0, -1 );
                        }
                        $this->mFunctionSynonyms[$sensitive][$syn] = $id;
@@ -4103,7 +4126,7 @@ class Parser
                        
                        if ( strpos( $matches[0], '%' ) !== false )
                                $matches[1] = urldecode( $matches[1] );
-                       $tp = Title::newFromText( $matches[1], NS_IMAGE );
+                       $tp = Title::newFromText( $matches[1]/*, NS_IMAGE*/ );
                        $nt =& $tp;
                        if( is_null( $nt ) ) {
                                # Bogus title. Ignore these so we don't bomb out later.
@@ -4218,13 +4241,14 @@ class Parser
                $params = array( 'frame' => array(), 'handler' => array(),
                        'horizAlign' => array(), 'vertAlign' => array() );
                foreach( $parts as $part ) {
+                       $part = trim( $part );
                        list( $magicName, $value ) = $mwArray->matchVariableStartToEnd( $part );
                        $validated = false;
                        if( isset( $paramMap[$magicName] ) ) {
                                list( $type, $paramName ) = $paramMap[$magicName];
 
                                // Special case; width and height come in one variable together
-                               if( $type == 'handler' && $paramName == 'width' ) {
+                               if( $type === 'handler' && $paramName === 'width' ) {
                                        $m = array();
                                        # (bug 13500) In both cases (width/height and width only),
                                        # permit trailing "px" for backward compatibility.
@@ -4247,7 +4271,7 @@ class Parser
                                                }
                                        } // else no validation -- bug 13436
                                } else {
-                                       if ( $type == 'handler' ) {
+                                       if ( $type === 'handler' ) {
                                                # Validate handler parameter
                                                $validated = $handler->validateParam( $paramName, $value );
                                        } else {
@@ -4392,7 +4416,7 @@ class Parser
                $sectionParts = explode( '-', $section );
                $sectionIndex = array_pop( $sectionParts );
                foreach ( $sectionParts as $part ) {
-                       if ( $part == 'T' ) {
+                       if ( $part === 'T' ) {
                                $flags |= self::PTD_FOR_INCLUSION;
                        }
                }
@@ -4409,14 +4433,14 @@ class Parser
                        $targetLevel = 1000;
                } else {
             while ( $node ) {
-                if ( $node->getName() == 'h' ) {
+                if ( $node->getName() === 'h' ) {
                     $bits = $node->splitHeading();
                                        if ( $bits['i'] == $sectionIndex ) {
                                        $targetLevel = $bits['level'];
                                                break;
                                        }
                                }
-                               if ( $mode == 'replace' ) {
+                               if ( $mode === 'replace' ) {
                                        $outText .= $frame->expand( $node, PPFrame::RECOVER_ORIG );
                                }
                                $node = $node->getNextSibling();
@@ -4425,7 +4449,7 @@ class Parser
 
                if ( !$node ) {
                        // Not found
-                       if ( $mode == 'get' ) {
+                       if ( $mode === 'get' ) {
                                return $newText;
                        } else {
                                return $text;
@@ -4434,21 +4458,21 @@ class Parser
 
                // Find the end of the section, including nested sections
                do {
-                       if ( $node->getName() == 'h' ) {
+                       if ( $node->getName() === 'h' ) {
                                $bits = $node->splitHeading();
                                $curLevel = $bits['level'];
                                if ( $bits['i'] != $sectionIndex && $curLevel <= $targetLevel ) {
                                        break;
                                }
                        }
-                       if ( $mode == 'get' ) {
+                       if ( $mode === 'get' ) {
                                $outText .= $frame->expand( $node, PPFrame::RECOVER_ORIG );
                        }
                        $node = $node->getNextSibling();
                } while ( $node );
 
                // Write out the remainder (in replace mode only)
-               if ( $mode == 'replace' ) {
+               if ( $mode === 'replace' ) {
                        // Output the replacement text
                        // Add two newlines on -- trailing whitespace in $newText is conventionally
                        // stripped by the editor, so we need both newlines to restore the paragraph gap
@@ -4678,7 +4702,7 @@ class StripState {
                do {
                        $oldText = $text;
                        $text = $this->general->replace( $text );
-               } while ( $text != $oldText );
+               } while ( $text !== $oldText );
                wfProfileOut( __METHOD__ );
                return $text;
        }
@@ -4688,7 +4712,7 @@ class StripState {
                do {
                        $oldText = $text;
                        $text = $this->nowiki->replace( $text );
-               } while ( $text != $oldText );
+               } while ( $text !== $oldText );
                wfProfileOut( __METHOD__ );
                return $text;
        }
@@ -4699,7 +4723,7 @@ class StripState {
                        $oldText = $text;
                        $text = $this->general->replace( $text );
                        $text = $this->nowiki->replace( $text );
-               } while ( $text != $oldText );
+               } while ( $text !== $oldText );
                wfProfileOut( __METHOD__ );
                return $text;
        }
@@ -4713,7 +4737,7 @@ class OnlyIncludeReplacer {
        var $output = '';
 
        function replace( $matches ) {
-               if ( substr( $matches[1], -1 ) == "\n" ) {
+               if ( substr( $matches[1], -1 ) === "\n" ) {
                        $this->output .= substr( $matches[1], 0, -1 );
                } else {
                        $this->output .= $matches[1];