From: Siebrand Mazeland Date: Sat, 4 Sep 2010 13:48:16 +0000 (+0000) Subject: More whitespace updates for files touched in r72342: X-Git-Tag: 1.31.0-rc.0~35154 X-Git-Url: http://git.cyclocoop.org/%24self?a=commitdiff_plain;h=64fdb18e74d72a079d7e2ad2b17830a6113d7f78;p=lhc%2Fweb%2Fwiklou.git More whitespace updates for files touched in r72342: * stylize.php run * code formatting updates * updated a few comments * added braces where not used --- diff --git a/includes/Exception.php b/includes/Exception.php index e89d94269b..22443624fd 100644 --- a/includes/Exception.php +++ b/includes/Exception.php @@ -32,11 +32,13 @@ class MWException extends Exception { */ function useMessageCache() { global $wgLang; + foreach ( $this->getTrace() as $frame ) { if ( isset( $frame['class'] ) && $frame['class'] === 'LocalisationCache' ) { return false; } } + return is_object( $wgLang ); } @@ -49,20 +51,26 @@ class MWException extends Exception { */ function runHooks( $name, $args = array() ) { global $wgExceptionHooks; - if( !isset( $wgExceptionHooks ) || !is_array( $wgExceptionHooks ) ) + + if ( !isset( $wgExceptionHooks ) || !is_array( $wgExceptionHooks ) ) { return; // Just silently ignore - if( !array_key_exists( $name, $wgExceptionHooks ) || !is_array( $wgExceptionHooks[ $name ] ) ) + } + + if ( !array_key_exists( $name, $wgExceptionHooks ) || !is_array( $wgExceptionHooks[ $name ] ) ) { return; + } + $hooks = $wgExceptionHooks[ $name ]; $callargs = array_merge( array( $this ), $args ); - foreach( $hooks as $hook ) { - if( is_string( $hook ) || ( is_array( $hook ) && count( $hook ) >= 2 && is_string( $hook[0] ) ) ) { //'function' or array( 'class', hook' ) + foreach ( $hooks as $hook ) { + if ( is_string( $hook ) || ( is_array( $hook ) && count( $hook ) >= 2 && is_string( $hook[0] ) ) ) { // 'function' or array( 'class', hook' ) $result = call_user_func_array( $hook, $callargs ); } else { $result = null; } - if( is_string( $result ) ) + + if ( is_string( $result ) ) return $result; } } @@ -78,6 +86,7 @@ class MWException extends Exception { */ function msg( $key, $fallback /*[, params...] */ ) { $args = array_slice( func_get_args(), 2 ); + if ( $this->useMessageCache() ) { return wfMsgReal( $key, $args ); } else { @@ -94,7 +103,8 @@ class MWException extends Exception { */ function getHTML() { global $wgShowExceptionDetails; - if( $wgShowExceptionDetails ) { + + if ( $wgShowExceptionDetails ) { return '

' . nl2br( htmlspecialchars( $this->getMessage() ) ) . '

Backtrace:

' . nl2br( htmlspecialchars( $this->getTraceAsString() ) ) . "

\n"; @@ -111,7 +121,8 @@ class MWException extends Exception { */ function getText() { global $wgShowExceptionDetails; - if( $wgShowExceptionDetails ) { + + if ( $wgShowExceptionDetails ) { return $this->getMessage() . "\nBacktrace:\n" . $this->getTraceAsString() . "\n"; } else { @@ -126,6 +137,7 @@ class MWException extends Exception { return wfMsg( 'internalerror' ); } else { global $wgSitename; + return "$wgSitename error"; } } @@ -138,9 +150,11 @@ class MWException extends Exception { */ function getLogMessage() { global $wgRequest; + $file = $this->getFile(); $line = $this->getLine(); $message = $this->getMessage(); + if ( isset( $wgRequest ) ) { $url = $wgRequest->getRequestURL(); if ( !$url ) { @@ -156,6 +170,7 @@ class MWException extends Exception { /** Output the exception report using HTML */ function reportHTML() { global $wgOut; + if ( $this->useOutputPage() ) { $wgOut->setPageTitle( $this->getPageTitle() ); $wgOut->setRobotPolicy( "noindex,nofollow" ); @@ -163,16 +178,19 @@ class MWException extends Exception { $wgOut->enableClientCache( false ); $wgOut->redirect( '' ); $wgOut->clearHTML(); - if( $hookResult = $this->runHooks( get_class( $this ) ) ) { + + if ( $hookResult = $this->runHooks( get_class( $this ) ) ) { $wgOut->addHTML( $hookResult ); } else { $wgOut->addHTML( $this->getHTML() ); } + $wgOut->output(); } else { - if( $hookResult = $this->runHooks( get_class( $this ) . "Raw" ) ) { + if ( $hookResult = $this->runHooks( get_class( $this ) . "Raw" ) ) { die( $hookResult ); } + if ( defined( 'MEDIAWIKI_INSTALL' ) || $this->htmlBodyOnly() ) { echo $this->getHTML(); } else { @@ -189,9 +207,11 @@ class MWException extends Exception { */ function report() { $log = $this->getLogMessage(); + if ( $log ) { wfDebugLog( 'exception', $log ); } + if ( self::isCommandLine() ) { wfPrintError( $this->getText() ); } else { @@ -208,11 +228,12 @@ class MWException extends Exception { if ( !headers_sent() ) { header( 'HTTP/1.0 500 Internal Server Error' ); - header( 'Content-type: text/html; charset='.$wgOutputEncoding ); + header( 'Content-type: text/html; charset=' . $wgOutputEncoding ); /* Don't cache error pages! They cause no end of trouble... */ header( 'Cache-control: none' ); header( 'Pragma: nocache' ); } + $title = $this->getPageTitle(); return " @@ -274,6 +295,7 @@ class ErrorPageError extends MWException { function report() { global $wgOut; + $wgOut->showErrorPage( $this->title, $this->msg ); $wgOut->output(); } @@ -293,6 +315,7 @@ function wfReportException( Exception $e ) { global $wgShowExceptionDetails; $cmdLine = MWException::isCommandLine(); + if ( $e instanceof MWException ) { try { $e->report(); @@ -301,6 +324,7 @@ function wfReportException( Exception $e ) { // Show a simpler error message for the original exception, // don't try to invoke report() $message = "MediaWiki internal error.\n\n"; + if ( $wgShowExceptionDetails ) { $message .= 'Original exception: ' . $e->__toString() . "\n\n" . 'Exception caught inside exception handler: ' . $e2->__toString(); @@ -309,23 +333,27 @@ function wfReportException( Exception $e ) { "Set \$wgShowExceptionDetails = true; at the bottom of LocalSettings.php " . "to show detailed debugging information."; } + $message .= "\n"; + if ( $cmdLine ) { wfPrintError( $message ); } else { - echo nl2br( htmlspecialchars( $message ) ). "\n"; + echo nl2br( htmlspecialchars( $message ) ) . "\n"; } } } else { $message = "Unexpected non-MediaWiki exception encountered, of type \"" . get_class( $e ) . "\"\n" . $e->__toString() . "\n"; + if ( $wgShowExceptionDetails ) { - $message .= "\n" . $e->getTraceAsString() ."\n"; + $message .= "\n" . $e->getTraceAsString() . "\n"; } + if ( $cmdLine ) { wfPrintError( $message ); } else { - echo nl2br( htmlspecialchars( $message ) ). "\n"; + echo nl2br( htmlspecialchars( $message ) ) . "\n"; } } } @@ -335,7 +363,7 @@ function wfReportException( Exception $e ) { * Use this in command line mode only (see isCommandLine) */ function wfPrintError( $message ) { - #NOTE: STDERR may not be available, especially if php-cgi is used from the command line (bug #15602). + # NOTE: STDERR may not be available, especially if php-cgi is used from the command line (bug #15602). # Try to produce meaningful output anyway. Using echo may corrupt output to STDOUT though. if ( defined( 'STDERR' ) ) { fwrite( STDERR, $message ); @@ -357,6 +385,7 @@ function wfPrintError( $message ) { */ function wfExceptionHandler( $e ) { global $wgFullyInitialised; + wfReportException( $e ); // Final cleanup, similar to wfErrorExit() diff --git a/includes/HttpFunctions.php b/includes/HttpFunctions.php index 9123cc679e..0dccebbd97 100644 --- a/includes/HttpFunctions.php +++ b/includes/HttpFunctions.php @@ -35,11 +35,14 @@ class Http { $url = wfExpandUrl( $url ); wfDebug( "HTTP: $method: $url\n" ); $options['method'] = strtoupper( $method ); + if ( !isset( $options['timeout'] ) ) { $options['timeout'] = 'default'; } + $req = HttpRequest::factory( $url, $options ); $status = $req->execute(); + if ( $status->isOK() ) { return $req->getContent(); } else { @@ -72,6 +75,7 @@ class Http { */ public static function isLocalURL( $url ) { global $wgCommandLineMode, $wgConf; + if ( $wgCommandLineMode ) { return false; } @@ -84,6 +88,7 @@ class Http { $domainParts = explode( '.', $host ); // Check if this domain or any superdomain is listed in $wgConf as a local virtual host $domainParts = array_reverse( $domainParts ); + for ( $i = 0; $i < count( $domainParts ); $i++ ) { $domainPart = $domainParts[$i]; if ( $i == 0 ) { @@ -91,11 +96,13 @@ class Http { } else { $domain = $domainPart . '.' . $domain; } + if ( $wgConf->isLocalVHost( $domain ) ) { return true; } } } + return false; } @@ -165,12 +172,12 @@ class HttpRequest { $this->parsedUrl = parse_url( $url ); if ( !Http::isValidURI( $this->url ) ) { - $this->status = Status::newFatal('http-invalid-url'); + $this->status = Status::newFatal( 'http-invalid-url' ); } else { $this->status = Status::newGood( 100 ); // continue } - if ( isset($options['timeout']) && $options['timeout'] != 'default' ) { + if ( isset( $options['timeout'] ) && $options['timeout'] != 'default' ) { $this->timeout = $options['timeout']; } else { $this->timeout = $wgHTTPTimeout; @@ -178,8 +185,9 @@ class HttpRequest { $members = array( "postData", "proxy", "noProxy", "sslVerifyHost", "caInfo", "method", "followRedirects", "maxRedirects", "sslVerifyCert" ); + foreach ( $members as $o ) { - if ( isset($options[$o]) ) { + if ( isset( $options[$o] ) ) { $this->$o = $options[$o]; } } @@ -193,21 +201,21 @@ class HttpRequest { if ( !Http::$httpEngine ) { Http::$httpEngine = function_exists( 'curl_init' ) ? 'curl' : 'php'; } elseif ( Http::$httpEngine == 'curl' && !function_exists( 'curl_init' ) ) { - throw new MWException( __METHOD__.': curl (http://php.net/curl) is not installed, but'. + throw new MWException( __METHOD__ . ': curl (http://php.net/curl) is not installed, but' . ' Http::$httpEngine is set to "curl"' ); } switch( Http::$httpEngine ) { - case 'curl': - return new CurlHttpRequest( $url, $options ); - case 'php': - if ( !wfIniGetBool( 'allow_url_fopen' ) ) { - throw new MWException( __METHOD__.': allow_url_fopen needs to be enabled for pure PHP'. - ' http requests to work. If possible, curl should be used instead. See http://php.net/curl.' ); - } - return new PhpHttpRequest( $url, $options ); - default: - throw new MWException( __METHOD__.': The setting of Http::$httpEngine is not valid.' ); + case 'curl': + return new CurlHttpRequest( $url, $options ); + case 'php': + if ( !wfIniGetBool( 'allow_url_fopen' ) ) { + throw new MWException( __METHOD__ . ': allow_url_fopen needs to be enabled for pure PHP' . + ' http requests to work. If possible, curl should be used instead. See http://php.net/curl.' ); + } + return new PhpHttpRequest( $url, $options ); + default: + throw new MWException( __METHOD__ . ': The setting of Http::$httpEngine is not valid.' ); } } @@ -226,7 +234,7 @@ class HttpRequest { * @param $args Array * @todo overload the args param */ - public function setData($args) { + public function setData( $args ) { $this->postData = $args; } @@ -242,6 +250,7 @@ class HttpRequest { if ( $this->proxy ) { return; } + if ( Http::isLocalURL( $this->url ) ) { $this->proxy = 'http://localhost:80/'; } elseif ( $wgHTTPProxy ) { @@ -255,20 +264,20 @@ class HttpRequest { * Set the refererer header */ public function setReferer( $url ) { - $this->setHeader('Referer', $url); + $this->setHeader( 'Referer', $url ); } /** * Set the user agent */ public function setUserAgent( $UA ) { - $this->setHeader('User-Agent', $UA); + $this->setHeader( 'User-Agent', $UA ); } /** * Set an arbitrary header */ - public function setHeader($name, $value) { + public function setHeader( $name, $value ) { // I feel like I should normalize the case here... $this->reqHeaders[$name] = $value; } @@ -279,14 +288,18 @@ class HttpRequest { public function getHeaderList() { $list = array(); - if( $this->cookieJar ) { + if ( $this->cookieJar ) { $this->reqHeaders['Cookie'] = - $this->cookieJar->serializeToHttpRequest($this->parsedUrl['path'], - $this->parsedUrl['host']); + $this->cookieJar->serializeToHttpRequest( + $this->parsedUrl['path'], + $this->parsedUrl['host'] + ); } - foreach($this->reqHeaders as $name => $value) { + + foreach ( $this->reqHeaders as $name => $value ) { $list[] = "$name: $value"; } + return $list; } @@ -321,7 +334,7 @@ class HttpRequest { $this->content = ""; - if( strtoupper($this->method) == "HEAD" ) { + if ( strtoupper( $this->method ) == "HEAD" ) { $this->headersOnly = true; } @@ -329,7 +342,7 @@ class HttpRequest { $this->postData = wfArrayToCGI( $this->postData ); } - if ( is_object( $wgTitle ) && !isset($this->reqHeaders['Referer']) ) { + if ( is_object( $wgTitle ) && !isset( $this->reqHeaders['Referer'] ) ) { $this->setReferer( $wgTitle->getFullURL() ); } @@ -341,8 +354,8 @@ class HttpRequest { $this->setCallback( array( $this, 'read' ) ); } - if ( !isset($this->reqHeaders['User-Agent']) ) { - $this->setUserAgent(Http::userAgent()); + if ( !isset( $this->reqHeaders['User-Agent'] ) ) { + $this->setUserAgent( Http::userAgent() ); } } @@ -355,14 +368,15 @@ class HttpRequest { */ protected function parseHeader() { $lastname = ""; - foreach( $this->headerList as $header ) { - if( preg_match( "#^HTTP/([0-9.]+) (.*)#", $header, $match ) ) { + + foreach ( $this->headerList as $header ) { + if ( preg_match( "#^HTTP/([0-9.]+) (.*)#", $header, $match ) ) { $this->respVersion = $match[1]; $this->respStatus = $match[2]; - } elseif( preg_match( "#^[ \t]#", $header ) ) { - $last = count($this->respHeaders[$lastname]) - 1; + } elseif ( preg_match( "#^[ \t]#", $header ) ) { + $last = count( $this->respHeaders[$lastname] ) - 1; $this->respHeaders[$lastname][$last] .= "\r\n$header"; - } elseif( preg_match( "#^([^:]*):[\t ]*(.*)#", $header, $match ) ) { + } elseif ( preg_match( "#^([^:]*):[\t ]*(.*)#", $header, $match ) ) { $this->respHeaders[strtolower( $match[1] )][] = $match[2]; $lastname = strtolower( $match[1] ); } @@ -378,13 +392,13 @@ class HttpRequest { * @return nothing */ protected function setStatus() { - if( !$this->respHeaders ) { + if ( !$this->respHeaders ) { $this->parseHeader(); } - if((int)$this->respStatus !== 200) { - list( $code, $message ) = explode(" ", $this->respStatus, 2); - $this->status->fatal("http-bad-status", $code, $message ); + if ( (int)$this->respStatus !== 200 ) { + list( $code, $message ) = explode( " ", $this->respStatus, 2 ); + $this->status->fatal( "http-bad-status", $code, $message ); } } @@ -395,14 +409,16 @@ class HttpRequest { * @return Boolean */ public function isRedirect() { - if( !$this->respHeaders ) { + if ( !$this->respHeaders ) { $this->parseHeader(); } $status = (int)$this->respStatus; + if ( $status >= 300 && $status <= 303 ) { return true; } + return false; } @@ -415,9 +431,10 @@ class HttpRequest { * @return Array */ public function getResponseHeaders() { - if( !$this->respHeaders ) { + if ( !$this->respHeaders ) { $this->parseHeader(); } + return $this->respHeaders; } @@ -427,14 +444,16 @@ class HttpRequest { * @param $header String * @return String */ - public function getResponseHeader($header) { - if( !$this->respHeaders ) { + public function getResponseHeader( $header ) { + if ( !$this->respHeaders ) { $this->parseHeader(); } + if ( isset( $this->respHeaders[strtolower ( $header ) ] ) ) { $v = $this->respHeaders[strtolower ( $header ) ]; return $v[count( $v ) - 1]; } + return null; } @@ -453,9 +472,10 @@ class HttpRequest { * @returns CookieJar */ public function getCookieJar() { - if( !$this->respHeaders ) { + if ( !$this->respHeaders ) { $this->parseHeader(); } + return $this->cookieJar; } @@ -465,23 +485,25 @@ class HttpRequest { * Set-Cookie headers. * @see Cookie::set */ - public function setCookie( $name, $value = null, $attr = null) { - if( !$this->cookieJar ) { + public function setCookie( $name, $value = null, $attr = null ) { + if ( !$this->cookieJar ) { $this->cookieJar = new CookieJar; } - $this->cookieJar->setCookie($name, $value, $attr); + + $this->cookieJar->setCookie( $name, $value, $attr ); } /** * Parse the cookies in the response headers and store them in the cookie jar. */ protected function parseCookies() { - if( !$this->cookieJar ) { + if ( !$this->cookieJar ) { $this->cookieJar = new CookieJar; } - if( isset( $this->respHeaders['set-cookie'] ) ) { + + if ( isset( $this->respHeaders['set-cookie'] ) ) { $url = parse_url( $this->getFinalUrl() ); - foreach( $this->respHeaders['set-cookie'] as $cookie ) { + foreach ( $this->respHeaders['set-cookie'] as $cookie ) { $this->cookieJar->parseCookieResponseHeader( $cookie, $url['host'] ); } } @@ -493,7 +515,8 @@ class HttpRequest { * @return String */ public function getFinalUrl() { - $location = $this->getResponseHeader("Location"); + $location = $this->getResponseHeader( "Location" ); + if ( $location ) { return $location; } @@ -541,21 +564,24 @@ class Cookie { */ public function set( $value, $attr ) { $this->value = $value; - if( isset( $attr['expires'] ) ) { + + if ( isset( $attr['expires'] ) ) { $this->isSessionKey = false; $this->expires = strtotime( $attr['expires'] ); } - if( isset( $attr['path'] ) ) { + + if ( isset( $attr['path'] ) ) { $this->path = $attr['path']; } else { $this->path = "/"; } - if( isset( $attr['domain'] ) ) { - if( self::validateCookieDomain( $attr['domain'] ) ) { + + if ( isset( $attr['domain'] ) ) { + if ( self::validateCookieDomain( $attr['domain'] ) ) { $this->domain = $attr['domain']; } } else { - throw new MWException("You must specify a domain."); + throw new MWException( "You must specify a domain." ); } } @@ -571,39 +597,48 @@ class Cookie { * @param $originDomain String: (optional) the domain the cookie originates from * @return Boolean */ - public static function validateCookieDomain( $domain, $originDomain = null) { + public static function validateCookieDomain( $domain, $originDomain = null ) { // Don't allow a trailing dot - if( substr( $domain, -1 ) == "." ) return false; + if ( substr( $domain, -1 ) == "." ) { + return false; + } - $dc = explode(".", $domain); + $dc = explode( ".", $domain ); // Only allow full, valid IP addresses - if( preg_match( '/^[0-9.]+$/', $domain ) ) { - if( count( $dc ) != 4 ) return false; + if ( preg_match( '/^[0-9.]+$/', $domain ) ) { + if ( count( $dc ) != 4 ) { + return false; + } - if( ip2long( $domain ) === false ) return false; + if ( ip2long( $domain ) === false ) { + return false; + } - if( $originDomain == null || $originDomain == $domain ) return true; + if ( $originDomain == null || $originDomain == $domain ) { + return true; + } } // Don't allow cookies for "co.uk" or "gov.uk", etc, but allow "supermarket.uk" - if( strrpos( $domain, "." ) - strlen( $domain ) == -3 ) { - if( (count($dc) == 2 && strlen( $dc[0] ) <= 2 ) - || (count($dc) == 3 && strlen( $dc[0] ) == "" && strlen( $dc[1] ) <= 2 ) ) { + if ( strrpos( $domain, "." ) - strlen( $domain ) == -3 ) { + if ( ( count( $dc ) == 2 && strlen( $dc[0] ) <= 2 ) + || ( count( $dc ) == 3 && strlen( $dc[0] ) == "" && strlen( $dc[1] ) <= 2 ) ) { return false; } - if( (count($dc) == 2 || (count($dc) == 3 && $dc[0] == "") ) - && preg_match( '/(com|net|org|gov|edu)\...$/', $domain) ) { + if ( ( count( $dc ) == 2 || ( count( $dc ) == 3 && $dc[0] == "" ) ) + && preg_match( '/(com|net|org|gov|edu)\...$/', $domain ) ) { return false; } } - if( $originDomain != null ) { - if( substr( $domain, 0, 1 ) != "." && $domain != $originDomain ) { + if ( $originDomain != null ) { + if ( substr( $domain, 0, 1 ) != "." && $domain != $originDomain ) { return false; } - if( substr( $domain, 0, 1 ) == "." + + if ( substr( $domain, 0, 1 ) == "." && substr_compare( $originDomain, $domain, -strlen( $domain ), strlen( $domain ), TRUE ) != 0 ) { return false; @@ -623,40 +658,42 @@ class Cookie { public function serializeToHttpRequest( $path, $domain ) { $ret = ""; - if( $this->canServeDomain( $domain ) + if ( $this->canServeDomain( $domain ) && $this->canServePath( $path ) && $this->isUnExpired() ) { - $ret = $this->name ."=". $this->value; + $ret = $this->name . "=" . $this->value; } return $ret; } protected function canServeDomain( $domain ) { - if( $domain == $this->domain - || ( strlen( $domain) > strlen( $this->domain ) - && substr( $this->domain, 0, 1) == "." + if ( $domain == $this->domain + || ( strlen( $domain ) > strlen( $this->domain ) + && substr( $this->domain, 0, 1 ) == "." && substr_compare( $domain, $this->domain, -strlen( $this->domain ), strlen( $this->domain ), TRUE ) == 0 ) ) { return true; } + return false; } protected function canServePath( $path ) { - if( $this->path && substr_compare( $this->path, $path, 0, strlen( $this->path ) ) == 0 ) { + if ( $this->path && substr_compare( $this->path, $path, 0, strlen( $this->path ) ) == 0 ) { return true; } + return false; } protected function isUnExpired() { - if( $this->isSessionKey || $this->expires > time() ) { + if ( $this->isSessionKey || $this->expires > time() ) { return true; } + return false; } - } class CookieJar { @@ -666,12 +703,13 @@ class CookieJar { * Set a cookie in the cookie jar. Make sure only one cookie per-name exists. * @see Cookie::set() */ - public function setCookie ($name, $value, $attr) { + public function setCookie ( $name, $value, $attr ) { /* cookies: case insensitive, so this should work. * We'll still send the cookies back in the same case we got them, though. */ - $index = strtoupper($name); - if( isset( $this->cookie[$index] ) ) { + $index = strtoupper( $name ); + + if ( isset( $this->cookie[$index] ) ) { $this->cookie[$index]->set( $value, $attr ); } else { $this->cookie[$index] = new Cookie( $name, $value, $attr ); @@ -684,12 +722,15 @@ class CookieJar { public function serializeToHttpRequest( $path, $domain ) { $cookies = array(); - foreach( $this->cookie as $c ) { + foreach ( $this->cookie as $c ) { $serialized = $c->serializeToHttpRequest( $path, $domain ); - if ( $serialized ) $cookies[] = $serialized; + + if ( $serialized ) { + $cookies[] = $serialized; + } } - return implode("; ", $cookies); + return implode( "; ", $cookies ); } /** @@ -700,34 +741,37 @@ class CookieJar { */ public function parseCookieResponseHeader ( $cookie, $domain ) { $len = strlen( "Set-Cookie:" ); + if ( substr_compare( "Set-Cookie:", $cookie, 0, $len, TRUE ) === 0 ) { $cookie = substr( $cookie, $len ); } $bit = array_map( 'trim', explode( ";", $cookie ) ); - if ( count($bit) >= 1 ) { - list($name, $value) = explode( "=", array_shift( $bit ), 2 ); + + if ( count( $bit ) >= 1 ) { + list( $name, $value ) = explode( "=", array_shift( $bit ), 2 ); $attr = array(); - foreach( $bit as $piece ) { + + foreach ( $bit as $piece ) { $parts = explode( "=", $piece ); - if( count( $parts ) > 1 ) { + if ( count( $parts ) > 1 ) { $attr[strtolower( $parts[0] )] = $parts[1]; } else { $attr[strtolower( $parts[0] )] = true; } } - if( !isset( $attr['domain'] ) ) { + if ( !isset( $attr['domain'] ) ) { $attr['domain'] = $domain; } elseif ( !Cookie::validateCookieDomain( $attr['domain'], $domain ) ) { return null; } + $this->setCookie( $name, $value, $attr ); } } } - /** * HttpRequest implemented using internal curl compiled into PHP */ @@ -747,19 +791,21 @@ class CurlHttpRequest extends HttpRequest { public function execute() { parent::execute(); + if ( !$this->status->isOK() ) { return $this->status; } + $this->curlOptions[CURLOPT_PROXY] = $this->proxy; $this->curlOptions[CURLOPT_TIMEOUT] = $this->timeout; $this->curlOptions[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0; $this->curlOptions[CURLOPT_WRITEFUNCTION] = $this->callback; - $this->curlOptions[CURLOPT_HEADERFUNCTION] = array($this, "readHeader"); + $this->curlOptions[CURLOPT_HEADERFUNCTION] = array( $this, "readHeader" ); $this->curlOptions[CURLOPT_MAXREDIRS] = $this->maxRedirects; $this->curlOptions[CURLOPT_ENCODING] = ""; # Enable compression /* not sure these two are actually necessary */ - if(isset($this->reqHeaders['Referer'])) { + if ( isset( $this->reqHeaders['Referer'] ) ) { $this->curlOptions[CURLOPT_REFERER] = $this->reqHeaders['Referer']; } $this->curlOptions[CURLOPT_USERAGENT] = $this->reqHeaders['User-Agent']; @@ -793,13 +839,15 @@ class CurlHttpRequest extends HttpRequest { $this->curlOptions[CURLOPT_HTTPHEADER] = $this->getHeaderList(); $curlHandle = curl_init( $this->url ); + if ( !curl_setopt_array( $curlHandle, $this->curlOptions ) ) { - throw new MWException("Error setting curl options."); + throw new MWException( "Error setting curl options." ); } + if ( $this->followRedirects && $this->canFollowRedirects() ) { if ( ! @curl_setopt( $curlHandle, CURLOPT_FOLLOWLOCATION, true ) ) { - wfDebug( __METHOD__.": Couldn't set CURLOPT_FOLLOWLOCATION. " . - "Probably safe_mode or open_basedir is set.\n"); + wfDebug( __METHOD__ . ": Couldn't set CURLOPT_FOLLOWLOCATION. " . + "Probably safe_mode or open_basedir is set.\n" ); // Continue the processing. If it were in curl_setopt_array, // processing would have halted on its entry } @@ -814,13 +862,14 @@ class CurlHttpRequest extends HttpRequest { $this->status->fatal( 'http-curl-error', curl_error( $curlHandle ) ); } } else { - $this->headerList = explode("\r\n", $this->headerText); + $this->headerList = explode( "\r\n", $this->headerText ); } curl_close( $curlHandle ); $this->parseHeader(); $this->setStatus(); + return $this->status; } @@ -829,10 +878,12 @@ class CurlHttpRequest extends HttpRequest { wfDebug( "Cannot follow redirects in safe mode\n" ); return false; } + if ( !defined( 'CURLOPT_REDIR_PROTOCOLS' ) ) { wfDebug( "Cannot follow redirects with libcurl < 7.19.4 due to CVE-2009-0037\n" ); return false; } + return true; } } @@ -875,7 +926,7 @@ class PhpHttpRequest extends HttpRequest { } $options['method'] = $this->method; - $options['header'] = implode("\r\n", $this->getHeaderList()); + $options['header'] = implode( "\r\n", $this->getHeaderList() ); // Note that at some future point we may want to support // HTTP/1.1, but we'd have to write support for chunking // in version of PHP < 5.3.1 @@ -891,7 +942,7 @@ class PhpHttpRequest extends HttpRequest { $oldTimeout = false; if ( version_compare( '5.2.1', phpversion(), '>' ) ) { - $oldTimeout = ini_set('default_socket_timeout', $this->timeout); + $oldTimeout = ini_set( 'default_socket_timeout', $this->timeout ); } else { $options['timeout'] = $this->timeout; } @@ -901,17 +952,21 @@ class PhpHttpRequest extends HttpRequest { $this->headerList = array(); $reqCount = 0; $url = $this->url; + do { $reqCount++; wfSuppressWarnings(); $fh = fopen( $url, "r", false, $context ); wfRestoreWarnings(); + if ( !$fh ) { break; } + $result = stream_get_meta_data( $fh ); $this->headerList = $result['wrapper_data']; $this->parseHeader(); + if ( !$manuallyRedirect || !$this->followRedirects ) { break; } @@ -921,16 +976,18 @@ class PhpHttpRequest extends HttpRequest { break; } # Check security of URL - $url = $this->getResponseHeader("Location"); + $url = $this->getResponseHeader( "Location" ); + if ( substr( $url, 0, 7 ) !== 'http://' ) { - wfDebug( __METHOD__.": insecure redirection\n" ); + wfDebug( __METHOD__ . ": insecure redirection\n" ); break; } } while ( true ); if ( $oldTimeout !== false ) { - ini_set('default_socket_timeout', $oldTimeout); + ini_set( 'default_socket_timeout', $oldTimeout ); } + $this->setStatus(); if ( $fh === false ) { @@ -943,13 +1000,15 @@ class PhpHttpRequest extends HttpRequest { return $this->status; } - if($this->status->isOK()) { + if ( $this->status->isOK() ) { while ( !feof( $fh ) ) { $buf = fread( $fh, 8192 ); + if ( $buf === false ) { $this->status->fatal( 'http-read-error' ); break; } + if ( strlen( $buf ) ) { call_user_func( $this->callback, $fh, $buf ); } diff --git a/includes/Skin.php b/includes/Skin.php index 94dfa7bd53..7295a83b3a 100644 --- a/includes/Skin.php +++ b/includes/Skin.php @@ -40,20 +40,24 @@ class Skin extends Linker { static function getSkinNames() { global $wgValidSkinNames; static $skinsInitialised = false; + if ( !$skinsInitialised ) { # Get a list of available skins # Build using the regular expression '^(.*).php$' # Array keys are all lower case, array value keep the case used by filename # wfProfileIn( __METHOD__ . '-init' ); + global $wgStyleDirectory; + $skinDir = dir( $wgStyleDirectory ); # while code from www.php.net - while( false !== ( $file = $skinDir->read() ) ) { + while ( false !== ( $file = $skinDir->read() ) ) { // Skip non-PHP files, hidden files, and '.dep' includes $matches = array(); - if( preg_match( '/^([^.]*)\.php$/', $file, $matches ) ) { + + if ( preg_match( '/^([^.]*)\.php$/', $file, $matches ) ) { $aSkin = $matches[1]; $wgValidSkinNames[strtolower( $aSkin )] = $aSkin; } @@ -73,10 +77,13 @@ class Skin extends Linker { */ public static function getUsableSkins() { global $wgSkipSkins; + $usableSkins = self::getSkinNames(); + foreach ( $wgSkipSkins as $skip ) { unset( $usableSkins[$skip] ); } + return $usableSkins; } @@ -89,15 +96,16 @@ class Skin extends Linker { */ static function normalizeKey( $key ) { global $wgDefaultSkin; + $skinNames = Skin::getSkinNames(); - if( $key == '' ) { + if ( $key == '' ) { // Don't return the default immediately; // in a misconfiguration we need to fall back. $key = $wgDefaultSkin; } - if( isset( $skinNames[$key] ) ) { + if ( isset( $skinNames[$key] ) ) { return $key; } @@ -109,13 +117,13 @@ class Skin extends Linker { 2 => 'cologneblue' ); - if( isset( $fallback[$key] ) ) { + if ( isset( $fallback[$key] ) ) { $key = $fallback[$key]; } - if( isset( $skinNames[$key] ) ) { + if ( isset( $skinNames[$key] ) ) { return $key; - } else if( isset( $skinNames[$wgDefaultSkin] ) ) { + } else if ( isset( $skinNames[$wgDefaultSkin] ) ) { return $wgDefaultSkin; } else { return 'vector'; @@ -140,13 +148,14 @@ class Skin extends Linker { if ( !class_exists( $className ) ) { // Preload base classes to work around APC/PHP5 bug $deps = "{$wgStyleDirectory}/{$skinName}.deps.php"; - if( file_exists( $deps ) ) { + + if ( file_exists( $deps ) ) { include_once( $deps ); } require_once( "{$wgStyleDirectory}/{$skinName}.php" ); # Check if we got if not failback to default skin - if( !class_exists( $className ) ) { + if ( !class_exists( $className ) ) { # DO NOT die if the class isn't found. This breaks maintenance # scripts and can cause a user account to be unrecoverable # except by SQL manipulation if a previously valid skin name @@ -176,7 +185,9 @@ class Skin extends Linker { if ( $wgOut->isQuickbarSuppressed() ) { return 0; } + $q = $wgUser->getOption( 'quickbar', 0 ); + return $q; } @@ -189,11 +200,11 @@ class Skin extends Linker { # should not matter, but Konqueror (3.5.9 at least) incorrectly # uses whichever one appears later in the HTML source. Make sure # apple-touch-icon is specified first to avoid this. - if( false !== $wgAppleTouchIcon ) { + if ( false !== $wgAppleTouchIcon ) { $out->addLink( array( 'rel' => 'apple-touch-icon', 'href' => $wgAppleTouchIcon ) ); } - if( false !== $wgFavicon ) { + if ( false !== $wgFavicon ) { $out->addLink( array( 'rel' => 'shortcut icon', 'href' => $wgFavicon ) ); } @@ -203,7 +214,7 @@ class Skin extends Linker { 'type' => 'application/opensearchdescription+xml', 'href' => wfScript( 'opensearch_desc' ), 'title' => wfMsgForContent( 'opensearch-desc' ), - )); + ) ); $this->addMetadataLinks( $out ); @@ -245,16 +256,17 @@ class Skin extends Linker { global $wgEnableDublinCoreRdf, $wgEnableCreativeCommonsRdf; global $wgRightsPage, $wgRightsUrl; - if( $out->isArticleRelated() ) { + if ( $out->isArticleRelated() ) { # note: buggy CC software only reads first "meta" link - if( $wgEnableCreativeCommonsRdf ) { + if ( $wgEnableCreativeCommonsRdf ) { $out->addMetadataLink( array( 'title' => 'Creative Commons', 'type' => 'application/rdf+xml', 'href' => $this->mTitle->getLocalURL( 'action=creativecommons' ) ) ); } - if( $wgEnableDublinCoreRdf ) { + + if ( $wgEnableDublinCoreRdf ) { $out->addMetadataLink( array( 'title' => 'Dublin Core', 'type' => 'application/rdf+xml', @@ -263,16 +275,19 @@ class Skin extends Linker { } } $copyright = ''; - if( $wgRightsPage ) { + if ( $wgRightsPage ) { $copy = Title::newFromText( $wgRightsPage ); - if( $copy ) { + + if ( $copy ) { $copyright = $copy->getLocalURL(); } } - if( !$copyright && $wgRightsUrl ) { + + if ( !$copyright && $wgRightsUrl ) { $copyright = $wgRightsUrl; } - if( $copyright ) { + + if ( $copyright ) { $out->addLink( array( 'rel' => 'copyright', 'href' => $copyright ) @@ -339,9 +354,9 @@ class Skin extends Linker { $out->out( "\n" ); wfProfileOut( __METHOD__ ); } - + static function makeVariablesScript( $data ) { - if( $data ) { + if ( $data ) { return Html::inlineScript( 'mediaWiki.config.set(' . json_encode( $data ) . ');' ); } else { return ''; @@ -359,6 +374,7 @@ class Skin extends Linker { # Weird back-compat stuff. $skinName = $skinName['skinname']; } + global $wgScript, $wgTitle, $wgStylePath, $wgUser, $wgScriptExtension; global $wgArticlePath, $wgScriptPath, $wgServer, $wgContLang, $wgLang; global $wgOut, $wgArticle; @@ -422,32 +438,34 @@ class Skin extends Linker { 'wgSiteName' => $wgSitename, 'wgCategories' => $wgOut->getCategories(), ); + if ( $wgContLang->hasVariants() ) { $vars['wgUserVariant'] = $wgContLang->getPreferredVariant(); } // if on upload page output the extension list & js_upload - if( SpecialPage::resolveAlias( $wgTitle->getDBkey() ) == 'Upload' ) { + if ( SpecialPage::resolveAlias( $wgTitle->getDBkey() ) == 'Upload' ) { global $wgFileExtensions; $vars['wgFileExtensions'] = $wgFileExtensions; } - if( $wgUseAjax && $wgEnableMWSuggest && !$wgUser->getOption( 'disablesuggest', false ) ) { + if ( $wgUseAjax && $wgEnableMWSuggest && !$wgUser->getOption( 'disablesuggest', false ) ) { $vars['wgMWSuggestTemplate'] = SearchEngine::getMWSuggestTemplate(); $vars['wgDBname'] = $wgDBname; $vars['wgSearchNamespaces'] = SearchEngine::userNamespaces( $wgUser ); $vars['wgMWSuggestMessages'] = array( wfMsg( 'search-mwsuggest-enabled' ), wfMsg( 'search-mwsuggest-disabled' ) ); } - foreach( $wgRestrictionTypes as $type ) { + foreach ( $wgRestrictionTypes as $type ) { $vars['wgRestriction' . ucfirst( $type )] = $wgTitle->getRestrictions( $type ); } if ( $wgOut->isArticleRelated() && $wgUseAjax && $wgAjaxWatch && $wgUser->isLoggedIn() ) { $msgs = (object)array(); + foreach ( array( 'watch', 'unwatch', 'watching', 'unwatching', 'tooltip-ca-watch', 'tooltip-ca-unwatch' ) as $msgName ) { - $msgs->{$msgName . 'Msg'} = wfMsg( $msgName ); + $msgs-> { $msgName . 'Msg' } = wfMsg( $msgName ); } $vars['wgAjaxWatch'] = $msgs; } @@ -471,18 +489,19 @@ class Skin extends Linker { public function userCanPreview( $action ) { global $wgRequest, $wgUser; - if( $action != 'submit' ) { + if ( $action != 'submit' ) { return false; } - if( !$wgRequest->wasPosted() ) { + if ( !$wgRequest->wasPosted() ) { return false; } - if( !$this->mTitle->userCanEditCssSubpage() ) { + if ( !$this->mTitle->userCanEditCssSubpage() ) { return false; } - if( !$this->mTitle->userCanEditJsSubpage() ) { + if ( !$this->mTitle->userCanEditJsSubpage() ) { return false; } + return $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ); } @@ -505,7 +524,8 @@ class Skin extends Linker { global $wgStylePath; wfProfileIn( __METHOD__ ); - if( !$skinName ) { + + if ( !$skinName ) { $skinName = $this->getSkinName(); } @@ -513,16 +533,20 @@ class Skin extends Linker { $s .= "var skin = '" . Xml::escapeJsString( $skinName ) . "';\n"; $s .= "var stylepath = '" . Xml::escapeJsString( $wgStylePath ) . "';"; $s .= "\n\n/* MediaWiki:Common.js */\n"; + $commonJs = wfMsgExt( 'common.js', 'content' ); + if ( !wfEmptyMsg( 'common.js', $commonJs ) ) { $s .= $commonJs; } $s .= "\n\n/* MediaWiki:" . ucfirst( $skinName ) . ".js */\n"; + // avoid inclusion of non defined user JavaScript (with custom skins only) // by checking for default message content $msgKey = ucfirst( $skinName ) . '.js'; $userJS = wfMsgExt( $msgKey, 'content' ); + if ( !wfEmptyMsg( $msgKey, $userJS ) ) { $s .= $userJS; } @@ -536,8 +560,10 @@ class Skin extends Linker { */ public function generateUserStylesheet() { wfProfileIn( __METHOD__ ); + $s = "/* generated user stylesheet */\n" . $this->reallyGenerateUserStylesheet(); + wfProfileOut( __METHOD__ ); return $s; } @@ -548,12 +574,15 @@ class Skin extends Linker { */ protected function reallyGenerateUserStylesheet() { global $wgUser; + $s = ''; - if( ( $undopt = $wgUser->getOption( 'underline' ) ) < 2 ) { + + if ( ( $undopt = $wgUser->getOption( 'underline' ) ) < 2 ) { $underline = $undopt ? 'underline' : 'none'; $s .= "a { text-decoration: $underline; }\n"; } - if( $wgUser->getOption( 'highlightbroken' ) ) { + + if ( $wgUser->getOption( 'highlightbroken' ) ) { $s .= "a.new, #quickbar a.new { color: #CC2200; }\n"; } else { $s .= <<getOption( 'justify' ) ) { + + if ( $wgUser->getOption( 'justify' ) ) { $s .= "#article, #bodyContent, #mw_content { text-align: justify; }\n"; } - if( !$wgUser->getOption( 'showtoc' ) ) { + + if ( !$wgUser->getOption( 'showtoc' ) ) { $s .= "#toc { display: none; }\n"; } - if( !$wgUser->getOption( 'editsection' ) ) { + + if ( !$wgUser->getOption( 'editsection' ) ) { $s .= ".editsection { display: none; }\n"; } + $fontstyle = $wgUser->getOption( 'editfont' ); + if ( $fontstyle !== 'default' ) { $s .= "textarea { font-family: $fontstyle; }\n"; } + return $s; } @@ -610,44 +645,51 @@ CSS; // If we use the site's dynamic CSS, throw that in, too // Per-site custom styles - if( $wgUseSiteCss ) { + if ( $wgUseSiteCss ) { global $wgHandheldStyle; + $query = wfArrayToCGI( self::getDynamicStylesheetQuery() ); # Site settings must override extension css! (bug 15025) $out->addStyle( self::makeNSUrl( 'Common.css', $query, NS_MEDIAWIKI ) ); $out->addStyle( self::makeNSUrl( 'Print.css', $query, NS_MEDIAWIKI ), 'print' ); - if( $wgHandheldStyle ) { + + if ( $wgHandheldStyle ) { $out->addStyle( self::makeNSUrl( 'Handheld.css', $query, NS_MEDIAWIKI ), 'handheld' ); } $out->addStyle( self::makeNSUrl( $this->getSkinName() . '.css', $query, NS_MEDIAWIKI ) ); } global $wgAllowUserCssPrefs; - if( $wgAllowUserCssPrefs ){ - if( $wgUser->isLoggedIn() ) { + + if ( $wgAllowUserCssPrefs ) { + if ( $wgUser->isLoggedIn() ) { // Ensure that logged-in users' generated CSS isn't clobbered // by anons' publicly cacheable generated CSS. $siteargs['smaxage'] = '0'; $siteargs['ts'] = $wgUser->mTouched; } + // Per-user styles based on preferences $siteargs['gen'] = 'css'; - if( ( $us = $wgRequest->getVal( 'useskin', '' ) ) !== '' ) { + + if ( ( $us = $wgRequest->getVal( 'useskin', '' ) ) !== '' ) { $siteargs['useskin'] = $us; } + $out->addStyle( self::makeUrl( '-', wfArrayToCGI( $siteargs ) ) ); } // Per-user custom style pages - if( $wgAllowUserCss && $wgUser->isLoggedIn() ) { + if ( $wgAllowUserCss && $wgUser->isLoggedIn() ) { $action = $wgRequest->getVal( 'action' ); + # If we're previewing the CSS page, use it - if( $this->mTitle->isCssSubpage() && $this->userCanPreview( $action ) ) { + if ( $this->mTitle->isCssSubpage() && $this->userCanPreview( $action ) ) { // @FIXME: properly escape the cdata! $out->addInlineStyle( $wgRequest->getText( 'wpTextbox1' ) ); } else { $names = array( 'common', $this->getSkinName() ); - foreach( $names as $name ) { + foreach ( $names as $name ) { $out->addStyle( self::makeUrl( $this->userpage . '/' . $name . '.css', 'action=raw&ctype=text/css' ) @@ -666,6 +708,7 @@ CSS; */ public static function getDynamicStylesheetQuery() { global $wgSquidMaxage; + return array( 'action' => 'raw', 'maxage' => $wgSquidMaxage, @@ -689,14 +732,17 @@ CSS; function getPageClasses( $title ) { $numeric = 'ns-' . $title->getNamespace(); - if( $title->getNamespace() == NS_SPECIAL ) { + + if ( $title->getNamespace() == NS_SPECIAL ) { $type = 'ns-special'; - } elseif( $title->isTalkPage() ) { + } elseif ( $title->isTalkPage() ) { $type = 'ns-talk'; } else { $type = 'ns-subject'; } + $name = Sanitizer::escapeClass( 'page-' . $title->getPrefixedText() ); + return "$numeric $type $name"; } @@ -724,7 +770,7 @@ CSS; $qb = $this->qbSetting(); $langlinks = $this->otherLanguages(); - if( $langlinks ) { + if ( $langlinks ) { $rows = 2; $borderhack = ''; } else { @@ -738,16 +784,18 @@ CSS; $shove = ( $qb != 0 ); $left = ( $qb == 1 || $qb == 3 ); - if( $wgContLang->isRTL() ) { + + if ( $wgContLang->isRTL() ) { $left = !$left; } - if( !$shove ) { + if ( !$shove ) { $s .= "\n" . $this->logoText() . ''; - } elseif( $left ) { + } elseif ( $left ) { $s .= $this->getQuickbarCompensator( $rows ); } + $l = $wgContLang->alignStart(); $s .= "\n"; @@ -766,16 +814,19 @@ CSS; if ( $shove && !$left ) { # Right $s .= $this->getQuickbarCompensator( $rows ); } + $s .= "\n\n\n"; $s .= "\n
\n"; $notice = wfGetSiteNotice(); - if( $notice ) { + + if ( $notice ) { $s .= "\n
$notice
\n"; } $s .= $this->pageTitle(); $s .= $this->pageSubtitle(); $s .= $this->getCategories(); + wfProfileOut( __METHOD__ ); return $s; } @@ -784,7 +835,7 @@ CSS; global $wgOut, $wgUseCategoryBrowser; global $wgContLang, $wgUser; - if( count( $wgOut->mCategoryLinks ) == 0 ) { + if ( count( $wgOut->mCategoryLinks ) == 0 ) { return ''; } @@ -800,6 +851,7 @@ CSS; $allCats = $wgOut->getCategoryLinks(); $s = ''; $colon = wfMsgExt( 'colon-separator', 'escapenoentities' ); + if ( !empty( $allCats['normal'] ) ) { $t = $embed . implode( "{$pop} {$sep} {$embed}" , $allCats['normal'] ) . $pop; @@ -812,12 +864,13 @@ CSS; # Hidden categories if ( isset( $allCats['hidden'] ) ) { if ( $wgUser->getBoolOption( 'showhiddencats' ) ) { - $class ='mw-hidden-cats-user-shown'; + $class = 'mw-hidden-cats-user-shown'; } elseif ( $this->mTitle->getNamespace() == NS_CATEGORY ) { $class = 'mw-hidden-cats-ns-shown'; } else { $class = 'mw-hidden-cats-hidden'; } + $s .= "
" . wfMsgExt( 'hidden-categories', array( 'parsemag', 'escapenoentities' ), count( $allCats['hidden'] ) ) . $colon . $embed . implode( "$pop $sep $embed", $allCats['hidden'] ) . $pop . @@ -826,7 +879,7 @@ CSS; # optional 'dmoz-like' category browser. Will be shown under the list # of categories an article belong to - if( $wgUseCategoryBrowser ) { + if ( $wgUseCategoryBrowser ) { $s .= '

'; # get a big array of the parents tree @@ -852,18 +905,21 @@ CSS; */ function drawCategoryBrowser( $tree, &$skin ) { $return = ''; - foreach( $tree as $element => $parent ) { - if( empty( $parent ) ) { + + foreach ( $tree as $element => $parent ) { + if ( empty( $parent ) ) { # element start a new list $return .= "\n"; } else { # grab the others elements $return .= $this->drawCategoryBrowser( $parent, $skin ) . ' > '; } + # add our current element to the list $eltitle = Title::newFromText( $element ); $return .= $skin->link( $eltitle, $eltitle->getText() ); } + return $return; } @@ -872,13 +928,14 @@ CSS; $classes = 'catlinks'; - // Check what we're showing global $wgOut, $wgUser; + + // Check what we're showing $allCats = $wgOut->getCategoryLinks(); $showHidden = $wgUser->getBoolOption( 'showhiddencats' ) || $this->mTitle->getNamespace() == NS_CATEGORY; - if( empty( $allCats['normal'] ) && !( !empty( $allCats['hidden'] ) && $showHidden ) ) { + if ( empty( $allCats['normal'] ) && !( !empty( $allCats['hidden'] ) && $showHidden ) ) { $classes .= ' catlinks-allhidden'; } @@ -906,10 +963,10 @@ CSS; protected function afterContentHook() { $data = ''; - if( wfRunHooks( 'SkinAfterContent', array( &$data, $this ) ) ) { + if ( wfRunHooks( 'SkinAfterContent', array( &$data, $this ) ) ) { // adding just some spaces shouldn't toggle the output // of the whole
, so we use trim() here - if( trim( $data ) != '' ) { + if ( trim( $data ) != '' ) { // Doing this here instead of in the skins to // ensure that the div has the same ID in all // skins @@ -931,11 +988,13 @@ CSS; */ protected function generateDebugHTML() { global $wgShowDebug, $wgOut; + if ( $wgShowDebug ) { $listInternals = $this->formatDebugHTML( $wgOut->mDebugtext ); return "\n
\nDebug data:
    " . $listInternals . "
\n"; } + return ''; } @@ -943,7 +1002,8 @@ CSS; $lines = explode( "\n", $debugText ); $curIdent = 0; $ret = '
  • '; - foreach( $lines as $line ) { + + foreach ( $lines as $line ) { $m = array(); $display = ltrim( $line ); $ident = strlen( $line ) - strlen( $display ); @@ -972,7 +1032,9 @@ CSS; $curIdent = $ident; } + $ret .= str_repeat( '
  • ', $curIdent ) . ''; + return $ret; } @@ -993,6 +1055,7 @@ CSS; function bottomScripts( $out ) { $bottomScriptText = "\n" . $out->getHeadScripts( $this ); wfRunHooks( 'SkinAfterBottomScripts', array( $this, &$bottomScriptText ) ); + return $bottomScriptText; } @@ -1021,11 +1084,14 @@ CSS; $s[] = $this->printableLink(); $disclaimer = $this->disclaimerLink(); # may be empty - if( $disclaimer ) { + + if ( $disclaimer ) { $s[] = $disclaimer; } + $privacy = $this->privacyLink(); # may be empty too - if( $privacy ) { + + if ( $privacy ) { $s[] = $privacy; } @@ -1033,13 +1099,15 @@ CSS; if ( $this->mTitle->getNamespace() == NS_FILE ) { $name = $this->mTitle->getDBkey(); $image = wfFindFile( $this->mTitle ); - if( $image ) { + + if ( $image ) { $link = htmlspecialchars( $image->getURL() ); $style = $this->getInternalLinkAttributes( $link, $name ); $s[] = "{$name}"; } } } + if ( 'history' == $action || isset( $diff ) || isset( $oldid ) ) { $s[] .= $this->link( $this->mTitle, @@ -1053,7 +1121,7 @@ CSS; if ( $wgUser->getNewtalk() ) { # do not show "You have new messages" text when we are viewing our # own talk page - if( !$this->mTitle->equals( $wgUser->getTalkPage() ) ) { + if ( !$this->mTitle->equals( $wgUser->getTalkPage() ) ) { $tl = $this->link( $wgUser->getTalkPage(), wfMsgHtml( 'newmessageslink' ), @@ -1069,7 +1137,7 @@ CSS; array( 'diff' => 'cur' ), array( 'known', 'noclasses' ) ); - $s[] = ''. wfMsg( 'youhavenewmessages', $tl, $dl ) . ''; + $s[] = '' . wfMsg( 'youhavenewmessages', $tl, $dl ) . ''; # disable caching $wgOut->setSquidMaxage( 0 ); $wgOut->enableClientCache( false ); @@ -1077,9 +1145,11 @@ CSS; } $undelete = $this->getUndeleteLink(); - if( !empty( $undelete ) ) { + + if ( !empty( $undelete ) ) { $s[] = $undelete; } + return $wgLang->pipeList( $s ); } @@ -1091,12 +1161,14 @@ CSS; if ( $wgUser->isAllowed( 'deletedhistory' ) && ( $this->mTitle->getArticleId() == 0 || $action == 'history' ) ) { $n = $this->mTitle->isDeleted(); + if ( $n ) { if ( $wgUser->isAllowed( 'undelete' ) ) { $msg = 'thisisdeleted'; } else { $msg = 'viewdeleted'; } + return wfMsg( $msg, $this->link( @@ -1109,6 +1181,7 @@ CSS; ); } } + return ''; } @@ -1122,8 +1195,8 @@ CSS; $s[] = "" . wfMsg( 'printableversion' ) . ''; } - if( $wgOut->isSyndicated() ) { - foreach( $wgFeedClasses as $format => $class ) { + if ( $wgOut->isSyndicated() ) { + foreach ( $wgFeedClasses as $format => $class ) { $feedurl = $wgRequest->escapeAppendQuery( "feed=$format" ); $s[] = "" . wfMsgHtml( "feed-$format" ) . ""; @@ -1146,36 +1219,43 @@ CSS; global $wgOut; $sub = $wgOut->getSubtitle(); + if ( $sub == '' ) { global $wgExtraSubtitle; $sub = wfMsgExt( 'tagline', 'parsemag' ) . $wgExtraSubtitle; } + $subpages = $this->subPageSubtitle(); $sub .= !empty( $subpages ) ? "

    $subpages" : ''; $s = "

    {$sub}

    \n"; + return $s; } function subPageSubtitle() { $subpages = ''; - if( !wfRunHooks( 'SkinSubPageSubtitle', array( &$subpages ) ) ) { + + if ( !wfRunHooks( 'SkinSubPageSubtitle', array( &$subpages ) ) ) { return $subpages; } global $wgOut; - if( $wgOut->isArticle() && MWNamespace::hasSubpages( $this->mTitle->getNamespace() ) ) { + + if ( $wgOut->isArticle() && MWNamespace::hasSubpages( $this->mTitle->getNamespace() ) ) { $ptext = $this->mTitle->getPrefixedText(); - if( preg_match( '/\//', $ptext ) ) { + if ( preg_match( '/\//', $ptext ) ) { $links = explode( '/', $ptext ); array_pop( $links ); $c = 0; $growinglink = ''; $display = ''; - foreach( $links as $link ) { + + foreach ( $links as $link ) { $growinglink .= $link; $display .= $link; $linkObj = Title::newFromText( $growinglink ); - if( is_object( $linkObj ) && $linkObj->exists() ) { + + if ( is_object( $linkObj ) && $linkObj->exists() ) { $getlink = $this->link( $linkObj, htmlspecialchars( $display ), @@ -1183,12 +1263,15 @@ CSS; array(), array( 'known', 'noclasses' ) ); + $c++; - if( $c > 1 ) { + + if ( $c > 1 ) { $subpages .= wfMsgExt( 'pipe-separator', 'escapenoentities' ); } else { $subpages .= '< '; } + $subpages .= $getlink; $display = ''; } else { @@ -1198,6 +1281,7 @@ CSS; } } } + return $subpages; } @@ -1215,8 +1299,9 @@ CSS; $logoutPage = $wgContLang->specialPage( 'Userlogout' ); $ret = ''; + if ( $wgUser->isAnon() ) { - if( $this->showIPinHeader() ) { + if ( $this->showIPinHeader() ) { $name = wfGetIP(); $talkLink = $this->link( $wgUser->getTalkPage(), @@ -1229,6 +1314,7 @@ CSS; $returnTo = $this->mTitle->getPrefixedDBkey(); $query = array(); + if ( $logoutPage != $returnTo ) { $query['returnto'] = $returnTo; } @@ -1256,6 +1342,7 @@ CSS; $this->specialLink( 'preferences' ), ) ); } + $ret = $wgLang->pipeList( array( $ret, $this->link( @@ -1278,6 +1365,7 @@ CSS; function searchForm() { global $wgRequest, $wgUseTwoButtonsSearchForm; + $search = $wgRequest->getText( 'search' ); $s = '
    \n" . ''; - if( $wgUseTwoButtonsSearchForm ) { + if ( $wgUseTwoButtonsSearchForm ) { $s .= ' \n"; } else { $s .= ' \n"; @@ -1312,18 +1400,19 @@ CSS; $s[] = $this->editThisPage(); $s[] = $this->historyLink(); } + # Many people don't like this dropdown box - #$s[] = $this->specialPagesList(); + # $s[] = $this->specialPagesList(); - if( $this->variantLinks() ) { + if ( $this->variantLinks() ) { $s[] = $this->variantLinks(); } - if( $this->extensionTabLinks() ) { + if ( $this->extensionTabLinks() ) { $s[] = $this->extensionTabLinks(); } - // FIXME: Is using Language::pipeList impossible here? Do not quite understand the use of the newline + // @todo FIXME: Is using Language::pipeList impossible here? Do not quite understand the use of the newline return implode( $s, wfMsgExt( 'pipe-separator', 'escapenoentities' ) . "\n" ); } @@ -1338,13 +1427,13 @@ CSS; $out = ''; $s = array(); wfRunHooks( 'SkinTemplateTabs', array( $this, &$tabs ) ); - foreach( $tabs as $tab ) { + foreach ( $tabs as $tab ) { $s[] = Xml::element( 'a', array( 'href' => $tab['href'] ), $tab['text'] ); } - if( count( $s ) ) { + if ( count( $s ) ) { global $wgLang; $out = wfMsgExt( 'pipe-separator' , 'escapenoentities' ); @@ -1360,13 +1449,17 @@ CSS; */ function variantLinks() { $s = ''; + /* show links to different language variants */ global $wgDisableLangConversion, $wgLang, $wgContLang; + $variants = $wgContLang->getVariants(); - if( !$wgDisableLangConversion && sizeof( $variants ) > 1 ) { - foreach( $variants as $code ) { + + if ( !$wgDisableLangConversion && sizeof( $variants ) > 1 ) { + foreach ( $variants as $code ) { $varname = $wgContLang->getVariantname( $code ); - if( $varname == 'disable' ) { + + if ( $varname == 'disable' ) { continue; } $s = $wgLang->pipeList( array( @@ -1375,6 +1468,7 @@ CSS; ) ); } } + return $s; } @@ -1385,31 +1479,33 @@ CSS; $s = ''; if ( $wgOut->isArticleRelated() ) { $element[] = '' . $this->editThisPage() . ''; + if ( $wgUser->isLoggedIn() ) { $element[] = $this->watchThisPage(); } + $element[] = $this->talkLink(); $element[] = $this->historyLink(); $element[] = $this->whatLinksHere(); $element[] = $this->watchPageLinksLink(); - if( $wgUseTrackbacks ) { + if ( $wgUseTrackbacks ) { $element[] = $this->trackbackLink(); } if ( $this->mTitle->getNamespace() == NS_USER || $this->mTitle->getNamespace() == NS_USER_TALK - ) - { + ) { $id = User::idFromName( $this->mTitle->getText() ); $ip = User::isIP( $this->mTitle->getText() ); # Both anons and non-anons have contributions list - if( $id || $ip ) { + if ( $id || $ip ) { $element[] = $this->userContribsLink(); } - if( $this->showEmailUser( $id ) ) { + + if ( $this->showEmailUser( $id ) ) { $element[] = $this->emailUserLink(); } } @@ -1418,17 +1514,21 @@ CSS; if ( $this->mTitle->getArticleId() ) { $s .= "\n
    "; + // Delete/protect/move links for privileged users - if( $wgUser->isAllowed( 'delete' ) ) { + if ( $wgUser->isAllowed( 'delete' ) ) { $s .= $this->deleteThisPage(); } - if( $wgUser->isAllowed( 'protect' ) ) { + + if ( $wgUser->isAllowed( 'protect' ) ) { $s .= $sep . $this->protectThisPage(); } - if( $wgUser->isAllowed( 'move' ) ) { + + if ( $wgUser->isAllowed( 'move' ) ) { $s .= $sep . $this->moveThisPage(); } } + $s .= "
    \n" . $this->otherLanguages(); } @@ -1441,34 +1541,40 @@ CSS; $oldid = $wgRequest->getVal( 'oldid' ); $diff = $wgRequest->getVal( 'diff' ); + if ( !$wgOut->isArticle() ) { return ''; } - if( !$wgArticle instanceof Article ) { + + if ( !$wgArticle instanceof Article ) { return ''; } + if ( isset( $oldid ) || isset( $diff ) ) { return ''; } + if ( 0 == $wgArticle->getID() ) { return ''; } $s = ''; + if ( !$wgDisableCounters ) { $count = $wgLang->formatNum( $wgArticle->getCount() ); + if ( $count ) { $s = wfMsgExt( 'viewcount', array( 'parseinline' ), $count ); } } - if( $wgMaxCredits != 0 ) { + if ( $wgMaxCredits != 0 ) { $s .= ' ' . Credits::getCredits( $wgArticle, $wgMaxCredits, $wgShowCreditsIfMax ); } else { $s .= $this->lastModified(); } - if( $wgPageShowWatchingUsers && $wgUser->getOption( 'shownumberswatching' ) ) { + if ( $wgPageShowWatchingUsers && $wgUser->getOption( 'shownumberswatching' ) ) { $dbr = wfGetDB( DB_SLAVE ); $res = $dbr->select( 'watchlist', @@ -1495,6 +1601,7 @@ CSS; if ( $type == 'detect' ) { $diff = $wgRequest->getVal( 'diff' ); $isCur = $wgArticle && $wgArticle->isCurrent(); + if ( is_null( $diff ) && !$isCur && wfMsgForContent( 'history_copyright' ) !== '-' ) { $type = 'history'; } else { @@ -1509,20 +1616,23 @@ CSS; } $out = ''; - if( $wgRightsPage ) { + + if ( $wgRightsPage ) { $title = Title::newFromText( $wgRightsPage ); $link = $this->linkKnown( $title, $wgRightsText ); - } elseif( $wgRightsUrl ) { + } elseif ( $wgRightsUrl ) { $link = $this->makeExternalLink( $wgRightsUrl, $wgRightsText ); - } elseif( $wgRightsText ) { + } elseif ( $wgRightsText ) { $link = $wgRightsText; } else { # Give up now return $out; } + // Allow for site and per-namespace customization of copyright notice. $forContent = true; - if( isset( $wgArticle ) ) { + + if ( isset( $wgArticle ) ) { wfRunHooks( 'SkinCopyrightFooter', array( $wgArticle->getTitle(), $type, &$msg, &$link, &$forContent ) ); } @@ -1531,26 +1641,33 @@ CSS; } else { $out .= wfMsg( $msg, $link ); } + return $out; } function getCopyrightIcon() { global $wgRightsUrl, $wgRightsText, $wgRightsIcon, $wgCopyrightIcon; + $out = ''; + if ( isset( $wgCopyrightIcon ) && $wgCopyrightIcon ) { $out = $wgCopyrightIcon; } elseif ( $wgRightsIcon ) { $icon = htmlspecialchars( $wgRightsIcon ); + if ( $wgRightsUrl ) { $url = htmlspecialchars( $wgRightsUrl ); - $out .= ''; + $out .= ''; } + $text = htmlspecialchars( $wgRightsText ); $out .= "\"$text\""; + if ( $wgRightsUrl ) { $out .= ''; } } + return $out; } @@ -1560,18 +1677,22 @@ CSS; */ function getPoweredBy() { global $wgStylePath; + $url = htmlspecialchars( "$wgStylePath/common/images/poweredby_mediawiki_88x31.png" ); $img = 'Powered by MediaWiki'; + return $img; } function lastModified() { global $wgLang, $wgArticle; - if( $this->mRevisionId && $this->mRevisionId != $wgArticle->getLatest() ) { + + if ( $this->mRevisionId && $this->mRevisionId != $wgArticle->getLatest() ) { $timestamp = Revision::getTimestampFromId( $wgArticle->getTitle(), $this->mRevisionId ); } else { $timestamp = $wgArticle->getTimestamp(); } + if ( $timestamp ) { $d = $wgLang->date( $timestamp, true ); $t = $wgLang->time( $timestamp, true ); @@ -1579,9 +1700,11 @@ CSS; } else { $s = ''; } + if ( wfGetLB()->getLaggedSlaveMode() ) { $s .= ' ' . wfMsg( 'laggedslavemode' ) . ''; } + return $s; } @@ -1598,6 +1721,7 @@ CSS; $logourl = $this->getLogo(); $s = ""; + return $s; } @@ -1606,7 +1730,9 @@ CSS; */ function specialPagesList() { global $wgContLang, $wgServer, $wgRedirectScript; + $pages = array_merge( SpecialPage::getRegularPages(), SpecialPage::getRestrictedPages() ); + foreach ( $pages as $name => $page ) { $pages[$name] = $page->getDescription(); } @@ -1625,9 +1751,11 @@ CSS; $p = $wgContLang->specialPage( $name ); $s .= "\n"; } + $s .= "\n"; $s .= "\n"; $s .= "
    \n"; + return $s; } @@ -1643,12 +1771,13 @@ CSS; array(), array( 'known', 'noclasses' ) ); + return $s; } private function footerLink( $desc, $page ) { // if the link description has been set to "-" in the default language, - if ( wfMsgForContent( $desc ) == '-') { + if ( wfMsgForContent( $desc ) == '-' ) { // then it is disabled, for all languages. return ''; } else { @@ -1656,6 +1785,7 @@ CSS; // language (which may or may not be the same as the default language), // but we make the link target be the one site-wide page. $title = Title::newFromText( wfMsgForContent( $page ) ); + return $this->linkKnown( $title, wfMsgExt( $desc, array( 'parsemag', 'escapenoentities' ) ) @@ -1690,9 +1820,9 @@ CSS; if ( !$wgOut->isArticleRelated() ) { $s = wfMsg( 'protectedpage' ); } else { - if( $this->mTitle->quickUserCan( 'edit' ) && $this->mTitle->exists() ) { + if ( $this->mTitle->quickUserCan( 'edit' ) && $this->mTitle->exists() ) { $t = wfMsg( 'editthispage' ); - } elseif( $this->mTitle->quickUserCan( 'create' ) && !$this->mTitle->exists() ) { + } elseif ( $this->mTitle->quickUserCan( 'create' ) && !$this->mTitle->exists() ) { $t = wfMsg( 'create-this-page' ); } else { $t = wfMsg( 'viewsource' ); @@ -1706,6 +1836,7 @@ CSS; array( 'known', 'noclasses' ) ); } + return $s; } @@ -1721,7 +1852,7 @@ CSS; $options = array( 'action' => 'edit' ); - if( $this->mRevisionId && ! $wgArticle->isCurrent() ) { + if ( $this->mRevisionId && ! $wgArticle->isCurrent() ) { $options['oldid'] = intval( $this->mRevisionId ); } @@ -1732,6 +1863,7 @@ CSS; global $wgUser, $wgRequest; $diff = $wgRequest->getVal( 'diff' ); + if ( $this->mTitle->getArticleId() && ( !$diff ) && $wgUser->isAllowed( 'delete' ) ) { $t = wfMsg( 'deletethispage' ); @@ -1745,6 +1877,7 @@ CSS; } else { $s = ''; } + return $s; } @@ -1752,7 +1885,8 @@ CSS; global $wgUser, $wgRequest; $diff = $wgRequest->getVal( 'diff' ); - if ( $this->mTitle->getArticleId() && ( ! $diff ) && $wgUser->isAllowed('protect') ) { + + if ( $this->mTitle->getArticleId() && ( ! $diff ) && $wgUser->isAllowed( 'protect' ) ) { if ( $this->mTitle->isProtected() ) { $text = wfMsg( 'unprotectthispage' ); $query = array( 'action' => 'unprotect' ); @@ -1771,6 +1905,7 @@ CSS; } else { $s = ''; } + return $s; } @@ -1799,6 +1934,7 @@ CSS; } else { $s = wfMsg( 'notanarticle' ); } + return $s; } @@ -1865,6 +2001,7 @@ CSS; function watchPageLinksLink() { global $wgOut; + if ( !$wgOut->isArticleRelated() ) { return '(' . wfMsg( 'notanarticle' ) . ')'; } else { @@ -1891,19 +2028,23 @@ CSS; } $a = $wgOut->getLanguageLinks(); + if ( 0 == count( $a ) ) { return ''; } $s = wfMsg( 'otherlanguages' ) . wfMsg( 'colon-separator' ); $first = true; - if( $wgContLang->isRTL() ) { + + if ( $wgContLang->isRTL() ) { $s .= ''; } - foreach( $a as $l ) { + + foreach ( $a as $l ) { if ( !$first ) { $s .= wfMsgExt( 'pipe-separator', 'escapenoentities' ); } + $first = false; $nt = Title::newFromText( $l ); @@ -1914,12 +2055,15 @@ CSS; if ( $text == '' ) { $text = $l; } + $style = $this->getExternalLinkAttributes(); $s .= "{$text}"; } - if( $wgContLang->isRTL() ) { + + if ( $wgContLang->isRTL() ) { $s .= ''; } + return $s; } @@ -1931,7 +2075,7 @@ CSS; $linkOptions = array(); - if( $this->mTitle->isTalkPage() ) { + if ( $this->mTitle->isTalkPage() ) { $link = $this->mTitle->getSubjectPage(); switch( $link->getNamespace() ) { case NS_MAIN: @@ -1946,7 +2090,7 @@ CSS; case NS_FILE: $text = wfMsg( 'imagepage' ); # Make link known if image exists, even if the desc. page doesn't. - if( wfFindFile( $link ) ) + if ( wfFindFile( $link ) ) $linkOptions[] = 'known'; break; case NS_MEDIAWIKI: @@ -1984,9 +2128,9 @@ CSS; # __NEWSECTIONLINK___ changes behaviour here # If it is present, the link points to this page, otherwise # it points to the talk page - if( $this->mTitle->isTalkPage() ) { + if ( $this->mTitle->isTalkPage() ) { $title = $this->mTitle; - } elseif( $wgOut->showNewSectionLink() ) { + } elseif ( $wgOut->showNewSectionLink() ) { $title = $this->mTitle; } else { $title = $this->mTitle->getTalkPage(); @@ -2007,12 +2151,12 @@ CSS; function getUploadLink() { global $wgUploadNavigationUrl; - if( $wgUploadNavigationUrl ) { + if ( $wgUploadNavigationUrl ) { # Using an empty class attribute to avoid automatic setting of "external" class - return $this->makeExternalLink( $wgUploadNavigationUrl, wfMsgHtml( 'upload' ), false, null, array( 'class' => '') ); + return $this->makeExternalLink( $wgUploadNavigationUrl, wfMsgHtml( 'upload' ), false, null, array( 'class' => '' ) ); } else { return $this->link( - SpecialPage::getTitleFor('Upload'), + SpecialPage::getTitleFor( 'Upload' ), wfMsgHtml( 'upload' ), array(), array(), @@ -2025,6 +2169,7 @@ CSS; static function makeMainPageUrl( $urlaction = '' ) { $title = Title::newMainPage(); self::checkTitle( $title, '' ); + return $title->getLocalURL( $urlaction ); } @@ -2047,6 +2192,7 @@ CSS; static function makeUrl( $name, $urlaction = '' ) { $title = Title::newFromText( $name ); self::checkTitle( $title, $name ); + return $title->getLocalURL( $urlaction ); } @@ -2066,6 +2212,7 @@ CSS; static function makeNSUrl( $name, $urlaction = '', $namespace = NS_MAIN ) { $title = Title::makeTitleSafe( $namespace, $name ); self::checkTitle( $title, $name ); + return $title->getLocalURL( $urlaction ); } @@ -2073,6 +2220,7 @@ CSS; static function makeUrlDetails( $name, $urlaction = '' ) { $title = Title::newFromText( $name ); self::checkTitle( $title, $name ); + return array( 'href' => $title->getLocalURL( $urlaction ), 'exists' => $title->getArticleID() != 0 ? true : false @@ -2085,6 +2233,7 @@ CSS; static function makeKnownUrlDetails( $name, $urlaction = '' ) { $title = Title::newFromText( $name ); self::checkTitle( $title, $name ); + return array( 'href' => $title->getLocalURL( $urlaction ), 'exists' => true @@ -2093,9 +2242,9 @@ CSS; # make sure we have some title to operate on static function checkTitle( &$title, $name ) { - if( !is_object( $title ) ) { + if ( !is_object( $title ) ) { $title = Title::newFromText( $name ); - if( !is_object( $title ) ) { + if ( !is_object( $title ) ) { $title = Title::newFromText( '--error: link target missing--' ); } } @@ -2128,6 +2277,7 @@ CSS; if ( $wgEnableSidebarCache ) { $parserMemc->set( $key, $bar, $wgSidebarCacheExpiry ); } + wfProfileOut( __METHOD__ ); return $bar; } @@ -2155,29 +2305,35 @@ CSS; $wikiBar = array(); # We need to handle the wikitext on a different variable, to avoid trying to do an array operation on text, which would be a fatal error. $heading = ''; - foreach( $lines as $line ) { - if( strpos( $line, '*' ) !== 0 ) { + + foreach ( $lines as $line ) { + if ( strpos( $line, '*' ) !== 0 ) { continue; } - if( strpos( $line, '**') !== 0 ) { + + if ( strpos( $line, '**' ) !== 0 ) { $heading = trim( $line, '* ' ); - if( !array_key_exists( $heading, $bar ) ) { + if ( !array_key_exists( $heading, $bar ) ) { $bar[$heading] = array(); } } else { $line = trim( $line, '* ' ); - if( strpos( $line, '|' ) !== false ) { // sanity check + + if ( strpos( $line, '|' ) !== false ) { // sanity check $line = array_map( 'trim', explode( '|', $line, 2 ) ); $link = wfMsgForContent( $line[0] ); - if( $link == '-' ) { + + if ( $link == '-' ) { continue; } $text = wfMsgExt( $line[1], 'parsemag' ); - if( wfEmptyMsg( $line[1], $text ) ) { + + if ( wfEmptyMsg( $line[1], $text ) ) { $text = $line[1]; } - if( wfEmptyMsg( $line[0], $link ) ) { + + if ( wfEmptyMsg( $line[0], $link ) ) { $link = $line[0]; } @@ -2185,6 +2341,7 @@ CSS; $href = $link; } else { $title = Title::newFromText( $link ); + if ( $title ) { $title = $title->fixSpecialName(); $href = $title->getLocalURL(); @@ -2199,15 +2356,16 @@ CSS; 'id' => 'n-' . strtr( $line[1], ' ', '-' ), 'active' => false ); - } else if ( (substr($line, 0, 2) == '{{') && (substr($line, -2) == '}}') ) { + } else if ( ( substr( $line, 0, 2 ) == '{{' ) && ( substr( $line, -2 ) == '}}' ) ) { global $wgParser, $wgTitle; - $line = substr($line, 2, strlen($line) - 4 ); + $line = substr( $line, 2, strlen( $line ) - 4 ); - if (is_null($wgParser->mOptions)) + if ( is_null( $wgParser->mOptions ) ) { $wgParser->mOptions = new ParserOptions(); + } - $wgParser->mOptions->setEditSection(false); + $wgParser->mOptions->setEditSection( false ); $wikiBar[$heading] = $wgParser->parse( wfMsgForContentNoTrans( $line ) , $wgTitle, $wgParser->mOptions )->getText(); } else { continue; @@ -2215,8 +2373,9 @@ CSS; } } - if ( count($wikiBar) > 0 ) - $bar = array_merge($bar, $wikiBar); + if ( count( $wikiBar ) > 0 ) { + $bar = array_merge( $bar, $wikiBar ); + } return $bar; } @@ -2239,14 +2398,15 @@ CSS; */ function getNewtalks() { global $wgUser, $wgOut; + $newtalks = $wgUser->getNewMessageLinks(); $ntl = ''; - if( count( $newtalks ) == 1 && $newtalks[0]['wiki'] === wfWikiID() ) { + if ( count( $newtalks ) == 1 && $newtalks[0]['wiki'] === wfWikiID() ) { $userTitle = $this->mUser->getUserPage(); $userTalkTitle = $userTitle->getTalkPage(); - if( !$userTalkTitle->equals( $this->mTitle ) ) { + if ( !$userTalkTitle->equals( $this->mTitle ) ) { $newMessagesLink = $this->link( $userTalkTitle, wfMsgHtml( 'newmessageslink' ), @@ -2271,11 +2431,12 @@ CSS; # Disable Squid cache $wgOut->setSquidMaxage( 0 ); } - } elseif( count( $newtalks ) ) { + } elseif ( count( $newtalks ) ) { // _>" " for BC <= 1.16 $sep = str_replace( '_', ' ', wfMsgHtml( 'newtalkseparator' ) ); $msgs = array(); - foreach( $newtalks as $newtalk ) { + + foreach ( $newtalks as $newtalk ) { $msgs[] = Xml::element( 'a', array( 'href' => $newtalk['link'] ), $newtalk['wiki'] @@ -2285,7 +2446,7 @@ CSS; $ntl = wfMsgHtml( 'youhavenewmessagesmulti', $parts ); $wgOut->setSquidMaxage( 0 ); } + return $ntl; } - } diff --git a/includes/db/Database.php b/includes/db/Database.php index adee63415c..6a4d92bf72 100644 --- a/includes/db/Database.php +++ b/includes/db/Database.php @@ -21,9 +21,9 @@ define( 'DEADLOCK_DELAY_MAX', 1500000 ); */ abstract class DatabaseBase implements DatabaseType { -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ # Variables -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ protected $mLastQuery = ''; protected $mDoneWrites = false; @@ -41,9 +41,9 @@ abstract class DatabaseBase implements DatabaseType { protected $mFakeSlaveLag = null, $mFakeMaster = false; protected $mDefaultBigSelects = null; -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ # Accessors -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ # These optionally set a variable and return the previous state /** @@ -258,7 +258,7 @@ abstract class DatabaseBase implements DatabaseType { * @return Boolean */ function getFlag( $flag ) { - return !!($this->mFlags & $flag); + return !!( $this->mFlags & $flag ); } /** @@ -269,7 +269,7 @@ abstract class DatabaseBase implements DatabaseType { } function getWikiID() { - if( $this->mTablePrefix ) { + if ( $this->mTablePrefix ) { return "{$this->mDBname}-{$this->mTablePrefix}"; } else { return $this->mDBname; @@ -288,9 +288,9 @@ abstract class DatabaseBase implements DatabaseType { } } -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ # Other functions -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ /** * Constructor. @@ -303,9 +303,10 @@ abstract class DatabaseBase implements DatabaseType { * @param $tablePrefix String: database table prefixes. By default use the prefix gave in LocalSettings.php */ function __construct( $server = false, $user = false, $password = false, $dbName = false, - $failFunction = false, $flags = 0, $tablePrefix = 'get from global' ) { - + $failFunction = false, $flags = 0, $tablePrefix = 'get from global' + ) { global $wgOut, $wgDBprefix, $wgCommandLineMode; + # Can't get a reference if it hasn't been set yet if ( !isset( $wgOut ) ) { $wgOut = null; @@ -350,8 +351,7 @@ abstract class DatabaseBase implements DatabaseType { * @param failFunction * @param $flags */ - static function newFromParams( $server, $user, $password, $dbName, $failFunction = false, $flags = 0 ) - { + static function newFromParams( $server, $user, $password, $dbName, $failFunction = false, $flags = 0 ) { wfDeprecated( __METHOD__ ); return new DatabaseMysql( $server, $user, $password, $dbName, $failFunction, $flags ); } @@ -441,7 +441,7 @@ abstract class DatabaseBase implements DatabaseType { # logging size most of the time. The substr is really just a sanity check. # Who's been wasting my precious column space? -- TS - #$profName = 'query: ' . $fname . ' ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 ); + # $profName = 'query: ' . $fname . ' ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 ); if ( $isMaster ) { $queryProf = 'query-m: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 ); @@ -450,6 +450,7 @@ abstract class DatabaseBase implements DatabaseType { $queryProf = 'query: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 ); $totalProf = 'DatabaseBase::query'; } + wfProfileIn( $totalProf ); wfProfileIn( $queryProf ); } @@ -457,14 +458,14 @@ abstract class DatabaseBase implements DatabaseType { $this->mLastQuery = $sql; if ( !$this->mDoneWrites && $this->isWriteQuery( $sql ) ) { // Set a flag indicating that writes have been done - wfDebug( __METHOD__.": Writes done: $sql\n" ); + wfDebug( __METHOD__ . ": Writes done: $sql\n" ); $this->mDoneWrites = true; } # Add a comment for easy SHOW PROCESSLIST interpretation - #if ( $fname ) { + # if ( $fname ) { global $wgUser; - if ( is_object( $wgUser ) && !($wgUser instanceof StubObject) ) { + if ( is_object( $wgUser ) && !( $wgUser instanceof StubObject ) ) { $userName = $wgUser->getName(); if ( mb_strlen( $userName ) > 15 ) { $userName = mb_substr( $userName, 0, 15 ) . '...'; @@ -473,27 +474,29 @@ abstract class DatabaseBase implements DatabaseType { } else { $userName = ''; } - $commentedSql = preg_replace('/\s/', " /* $fname $userName */ ", $sql, 1); - #} else { + $commentedSql = preg_replace( '/\s/', " /* $fname $userName */ ", $sql, 1 ); + # } else { # $commentedSql = $sql; - #} + # } # If DBO_TRX is set, start a transaction if ( ( $this->mFlags & DBO_TRX ) && !$this->trxLevel() && - $sql != 'BEGIN' && $sql != 'COMMIT' && $sql != 'ROLLBACK') { + $sql != 'BEGIN' && $sql != 'COMMIT' && $sql != 'ROLLBACK' ) { // avoid establishing transactions for SHOW and SET statements too - // that would delay transaction initializations to once connection // is really used by application - $sqlstart = substr($sql,0,10); // very much worth it, benchmark certified(tm) - if (strpos($sqlstart,"SHOW ")!==0 and strpos($sqlstart,"SET ")!==0) + $sqlstart = substr( $sql, 0, 10 ); // very much worth it, benchmark certified(tm) + if ( strpos( $sqlstart, "SHOW " ) !== 0 and strpos( $sqlstart, "SET " ) !== 0 ) $this->begin(); } if ( $this->debug() ) { static $cnt = 0; + $cnt++; $sqlx = substr( $commentedSql, 0, 500 ); $sqlx = strtr( $sqlx, "\t\n", ' ' ); + if ( $isMaster ) { wfDebug( "Query $cnt (master): $sqlx\n" ); } else { @@ -513,12 +516,13 @@ abstract class DatabaseBase implements DatabaseType { # Transaction is gone, like it or not $this->mTrxLevel = 0; wfDebug( "Connection lost, reconnecting...\n" ); + if ( $this->ping() ) { wfDebug( "Reconnected\n" ); $sqlx = substr( $commentedSql, 0, 500 ); $sqlx = strtr( $sqlx, "\t\n", ' ' ); global $wgRequestTime; - $elapsed = round( microtime(true) - $wgRequestTime, 3 ); + $elapsed = round( microtime( true ) - $wgRequestTime, 3 ); wfLogDBError( "Connection lost and reconnected after {$elapsed}s, query: $sqlx\n" ); $ret = $this->doQuery( $commentedSql ); } else { @@ -534,6 +538,7 @@ abstract class DatabaseBase implements DatabaseType { wfProfileOut( $queryProf ); wfProfileOut( $totalProf ); } + return $this->resultObject( $ret ); } @@ -549,13 +554,13 @@ abstract class DatabaseBase implements DatabaseType { $ignore = $this->ignoreErrors( true ); ++$this->mErrorCount; - if( $ignore || $tempIgnore ) { - wfDebug("SQL ERROR (ignored): $error\n"); + if ( $ignore || $tempIgnore ) { + wfDebug( "SQL ERROR (ignored): $error\n" ); $this->ignoreErrors( $ignore ); } else { $sql1line = str_replace( "\n", "\\n", $sql ); - wfLogDBError("$fname\t{$this->mServer}\t$errno\t$error\t$sql1line\n"); - wfDebug("SQL ERROR: " . $error . "\n"); + wfLogDBError( "$fname\t{$this->mServer}\t$errno\t$error\t$sql1line\n" ); + wfDebug( "SQL ERROR: " . $error . "\n" ); throw new DBQueryError( $this, $error, $errno, $sql, $fname ); } } @@ -587,12 +592,14 @@ abstract class DatabaseBase implements DatabaseType { * @param $args Mixed: Either an array here, or put scalars as varargs */ function execute( $prepared, $args = null ) { - if( !is_array( $args ) ) { + if ( !is_array( $args ) ) { # Pull the var args $args = func_get_args(); array_shift( $args ); } + $sql = $this->fillPrepared( $prepared['query'], $args ); + return $this->query( $sql, $prepared['func'] ); } @@ -604,13 +611,16 @@ abstract class DatabaseBase implements DatabaseType { */ function safeQuery( $query, $args = null ) { $prepared = $this->prepare( $query, 'DatabaseBase::safeQuery' ); - if( !is_array( $args ) ) { + + if ( !is_array( $args ) ) { # Pull the var args $args = func_get_args(); array_shift( $args ); } + $retval = $this->execute( $prepared, $args ); $this->freePrepared( $prepared ); + return $retval; } @@ -624,6 +634,7 @@ abstract class DatabaseBase implements DatabaseType { function fillPrepared( $preparedQuery, $args ) { reset( $args ); $this->preparedArgs =& $args; + return preg_replace_callback( '/(\\\\[?!&]|[?!&])/', array( &$this, 'fillPreparedArg' ), $preparedQuery ); } @@ -643,7 +654,9 @@ abstract class DatabaseBase implements DatabaseType { case '\\!': return '!'; case '\\&': return '&'; } + list( /* $n */ , $arg ) = each( $this->preparedArgs ); + switch( $matches[1] ) { case '?': return $this->addQuotes( $arg ); case '!': return $arg; @@ -676,6 +689,7 @@ abstract class DatabaseBase implements DatabaseType { $table = $this->tableName( $table ); $sql = "UPDATE $table SET $var = '" . $this->strencode( $value ) . "' WHERE ($cond)"; + return (bool)$this->query( $sql, $fname ); } @@ -684,17 +698,21 @@ abstract class DatabaseBase implements DatabaseType { * Usually aborts on failure * If errors are explicitly ignored, returns FALSE on failure */ - function selectField( $table, $var, $cond='', $fname = 'DatabaseBase::selectField', $options = array() ) { + function selectField( $table, $var, $cond = '', $fname = 'DatabaseBase::selectField', $options = array() ) { if ( !is_array( $options ) ) { $options = array( $options ); } + $options['LIMIT'] = 1; $res = $this->select( $table, $var, $cond, $fname, $options ); + if ( $res === false || !$this->numRows( $res ) ) { return false; } + $row = $this->fetchRow( $res ); + if ( $row !== false ) { return reset( $row ); } else { @@ -717,6 +735,7 @@ abstract class DatabaseBase implements DatabaseType { $startOpts = ''; $noKeyOptions = array(); + foreach ( $options as $key => $option ) { if ( is_numeric( $key ) ) { $noKeyOptions[$option] = true; @@ -726,25 +745,29 @@ abstract class DatabaseBase implements DatabaseType { if ( isset( $options['GROUP BY'] ) ) { $preLimitTail .= " GROUP BY {$options['GROUP BY']}"; } + if ( isset( $options['HAVING'] ) ) { $preLimitTail .= " HAVING {$options['HAVING']}"; } + if ( isset( $options['ORDER BY'] ) ) { $preLimitTail .= " ORDER BY {$options['ORDER BY']}"; } - //if (isset($options['LIMIT'])) { + // if (isset($options['LIMIT'])) { // $tailOpts .= $this->limitResult('', $options['LIMIT'], // isset($options['OFFSET']) ? $options['OFFSET'] // : false); - //} + // } if ( isset( $noKeyOptions['FOR UPDATE'] ) ) { $postLimitTail .= ' FOR UPDATE'; } + if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) { $postLimitTail .= ' LOCK IN SHARE MODE'; } + if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) { $startOpts .= 'DISTINCT'; } @@ -753,24 +776,31 @@ abstract class DatabaseBase implements DatabaseType { if ( isset( $noKeyOptions['STRAIGHT_JOIN'] ) ) { $startOpts .= ' /*! STRAIGHT_JOIN */'; } + if ( isset( $noKeyOptions['HIGH_PRIORITY'] ) ) { $startOpts .= ' HIGH_PRIORITY'; } + if ( isset( $noKeyOptions['SQL_BIG_RESULT'] ) ) { $startOpts .= ' SQL_BIG_RESULT'; } + if ( isset( $noKeyOptions['SQL_BUFFER_RESULT'] ) ) { $startOpts .= ' SQL_BUFFER_RESULT'; } + if ( isset( $noKeyOptions['SQL_SMALL_RESULT'] ) ) { $startOpts .= ' SQL_SMALL_RESULT'; } + if ( isset( $noKeyOptions['SQL_CALC_FOUND_ROWS'] ) ) { $startOpts .= ' SQL_CALC_FOUND_ROWS'; } + if ( isset( $noKeyOptions['SQL_CACHE'] ) ) { $startOpts .= ' SQL_CACHE'; } + if ( isset( $noKeyOptions['SQL_NO_CACHE'] ) ) { $startOpts .= ' SQL_NO_CACHE'; } @@ -797,8 +827,9 @@ abstract class DatabaseBase implements DatabaseType { * (e.g. array( 'page' => array('LEFT JOIN','page_latest=rev_id') ) * @return mixed Database result resource (feed to DatabaseBase::fetchObject or whatever), or false on failure */ - function select( $table, $vars, $conds='', $fname = 'DatabaseBase::select', $options = array(), $join_conds = array() ) { + function select( $table, $vars, $conds = '', $fname = 'DatabaseBase::select', $options = array(), $join_conds = array() ) { $sql = $this->selectSQLText( $table, $vars, $conds, $fname, $options, $join_conds ); + return $this->query( $sql, $fname ); } @@ -815,21 +846,23 @@ abstract class DatabaseBase implements DatabaseType { * (e.g. array( 'page' => array('LEFT JOIN','page_latest=rev_id') ) * @return string, the SQL text */ - function selectSQLText( $table, $vars, $conds='', $fname = 'DatabaseBase::select', $options = array(), $join_conds = array() ) { - if( is_array( $vars ) ) { + function selectSQLText( $table, $vars, $conds = '', $fname = 'DatabaseBase::select', $options = array(), $join_conds = array() ) { + if ( is_array( $vars ) ) { $vars = implode( ',', $vars ); } - if( !is_array( $options ) ) { + + if ( !is_array( $options ) ) { $options = array( $options ); } - if( is_array( $table ) ) { - if ( !empty($join_conds) || ( isset( $options['USE INDEX'] ) && is_array( @$options['USE INDEX'] ) ) ) { + + if ( is_array( $table ) ) { + if ( !empty( $join_conds ) || ( isset( $options['USE INDEX'] ) && is_array( @$options['USE INDEX'] ) ) ) { $from = ' FROM ' . $this->tableNamesWithUseIndexOrJOIN( $table, @$options['USE INDEX'], $join_conds ); } else { $from = ' FROM ' . implode( ',', array_map( array( &$this, 'tableName' ), $table ) ); } - } elseif ($table!='') { - if ($table{0}==' ') { + } elseif ( $table != '' ) { + if ( $table { 0 } == ' ' ) { $from = ' FROM ' . $table; } else { $from = ' FROM ' . $this->tableName( $table ); @@ -840,7 +873,7 @@ abstract class DatabaseBase implements DatabaseType { list( $startOpts, $useIndex, $preLimitTail, $postLimitTail ) = $this->makeSelectOptions( $options ); - if( !empty( $conds ) ) { + if ( !empty( $conds ) ) { if ( is_array( $conds ) ) { $conds = $this->makeList( $conds, LIST_AND ); } @@ -849,14 +882,15 @@ abstract class DatabaseBase implements DatabaseType { $sql = "SELECT $startOpts $vars $from $useIndex $preLimitTail"; } - if (isset($options['LIMIT'])) - $sql = $this->limitResult($sql, $options['LIMIT'], - isset($options['OFFSET']) ? $options['OFFSET'] : false); + if ( isset( $options['LIMIT'] ) ) + $sql = $this->limitResult( $sql, $options['LIMIT'], + isset( $options['OFFSET'] ) ? $options['OFFSET'] : false ); $sql = "$sql $postLimitTail"; - if (isset($options['EXPLAIN'])) { + if ( isset( $options['EXPLAIN'] ) ) { $sql = 'EXPLAIN ' . $sql; } + return $sql; } @@ -881,13 +915,17 @@ abstract class DatabaseBase implements DatabaseType { function selectRow( $table, $vars, $conds, $fname = 'DatabaseBase::selectRow', $options = array(), $join_conds = array() ) { $options['LIMIT'] = 1; $res = $this->select( $table, $vars, $conds, $fname, $options, $join_conds ); + if ( $res === false ) { return false; } - if ( !$this->numRows($res) ) { + + if ( !$this->numRows( $res ) ) { return false; } + $obj = $this->fetchObject( $res ); + return $obj; } @@ -904,13 +942,15 @@ abstract class DatabaseBase implements DatabaseType { * @param $options Array: options for select * @return Integer: row count */ - public function estimateRowCount( $table, $vars='*', $conds='', $fname = 'DatabaseBase::estimateRowCount', $options = array() ) { + public function estimateRowCount( $table, $vars = '*', $conds = '', $fname = 'DatabaseBase::estimateRowCount', $options = array() ) { $rows = 0; $res = $this->select ( $table, 'COUNT(*) AS rowcount', $conds, $fname, $options ); + if ( $res ) { $row = $this->fetchRow( $res ); $rows = ( isset( $row['rowcount'] ) ) ? $row['rowcount'] : 0; } + return $rows; } @@ -925,17 +965,17 @@ abstract class DatabaseBase implements DatabaseType { # as to avoid crashing php on some large strings. # $sql = preg_replace ( "/'([^\\\\']|\\\\.)*'|\"([^\\\\\"]|\\\\.)*\"/", "'X'", $sql); - $sql = str_replace ( "\\\\", '', $sql); - $sql = str_replace ( "\\'", '', $sql); - $sql = str_replace ( "\\\"", '', $sql); - $sql = preg_replace ("/'.*'/s", "'X'", $sql); - $sql = preg_replace ('/".*"/s', "'X'", $sql); + $sql = str_replace ( "\\\\", '', $sql ); + $sql = str_replace ( "\\'", '', $sql ); + $sql = str_replace ( "\\\"", '', $sql ); + $sql = preg_replace ( "/'.*'/s", "'X'", $sql ); + $sql = preg_replace ( '/".*"/s', "'X'", $sql ); # All newlines, tabs, etc replaced by single space - $sql = preg_replace ( '/\s+/', ' ', $sql); + $sql = preg_replace ( '/\s+/', ' ', $sql ); # All numbers => N - $sql = preg_replace ('/-?[0-9]+/s', 'N', $sql); + $sql = preg_replace ( '/-?[0-9]+/s', 'N', $sql ); return $sql; } @@ -950,6 +990,7 @@ abstract class DatabaseBase implements DatabaseType { */ function fieldExists( $table, $field, $fname = 'DatabaseBase::fieldExists' ) { $info = $this->fieldInfo( $table, $field ); + return (bool)$info; } @@ -978,20 +1019,22 @@ abstract class DatabaseBase implements DatabaseType { # http://dev.mysql.com/doc/mysql/en/SHOW_INDEX.html $table = $this->tableName( $table ); $index = $this->indexName( $index ); - $sql = 'SHOW INDEX FROM '.$table; + $sql = 'SHOW INDEX FROM ' . $table; $res = $this->query( $sql, $fname ); + if ( !$res ) { return null; } $result = array(); + while ( $row = $this->fetchObject( $res ) ) { if ( $row->Key_name == $index ) { $result[] = $row; } } - return empty($result) ? false : $result; + return empty( $result ) ? false : $result; } /** @@ -1002,6 +1045,7 @@ abstract class DatabaseBase implements DatabaseType { $old = $this->ignoreErrors( true ); $res = $this->query( "SELECT 1 FROM $table LIMIT 1" ); $this->ignoreErrors( $old ); + return (bool)$res; } @@ -1012,6 +1056,7 @@ abstract class DatabaseBase implements DatabaseType { if ( $res instanceof ResultWrapper ) { $res = $res->result; } + return mysql_field_type( $res, $index ); } @@ -1020,12 +1065,14 @@ abstract class DatabaseBase implements DatabaseType { */ function indexUnique( $table, $index ) { $indexInfo = $this->indexInfo( $table, $index ); + if ( !$indexInfo ) { return null; } + return !$indexInfo[0]->Non_unique; } - + /** * INSERT wrapper, inserts an array into a table * @@ -1049,9 +1096,11 @@ abstract class DatabaseBase implements DatabaseType { } $table = $this->tableName( $table ); + if ( !is_array( $options ) ) { $options = array( $options ); } + if ( isset( $a[0] ) && is_array( $a[0] ) ) { $multi = true; $keys = array_keys( $a[0] ); @@ -1088,17 +1137,21 @@ abstract class DatabaseBase implements DatabaseType { * @return string */ function makeUpdateOptions( $options ) { - if( !is_array( $options ) ) { + if ( !is_array( $options ) ) { $options = array( $options ); } + $opts = array(); + if ( in_array( 'LOW_PRIORITY', $options ) ) { $opts[] = $this->lowPriorityOption(); } + if ( in_array( 'IGNORE', $options ) ) { $opts[] = 'IGNORE'; } - return implode(' ', $opts); + + return implode( ' ', $opts ); } /** @@ -1117,9 +1170,11 @@ abstract class DatabaseBase implements DatabaseType { $table = $this->tableName( $table ); $opts = $this->makeUpdateOptions( $options ); $sql = "UPDATE $opts $table SET " . $this->makeList( $values, LIST_SET ); + if ( $conds != '*' ) { $sql .= " WHERE " . $this->makeList( $conds, LIST_AND ); } + return $this->query( $sql, $fname ); } @@ -1139,11 +1194,12 @@ abstract class DatabaseBase implements DatabaseType { $first = true; $list = ''; + foreach ( $a as $field => $value ) { if ( !$first ) { if ( $mode == LIST_AND ) { $list .= ' AND '; - } elseif($mode == LIST_OR) { + } elseif ( $mode == LIST_OR ) { $list .= ' OR '; } else { $list .= ','; @@ -1151,21 +1207,22 @@ abstract class DatabaseBase implements DatabaseType { } else { $first = false; } - if ( ($mode == LIST_AND || $mode == LIST_OR) && is_numeric( $field ) ) { + + if ( ( $mode == LIST_AND || $mode == LIST_OR ) && is_numeric( $field ) ) { $list .= "($value)"; - } elseif ( ($mode == LIST_SET) && is_numeric( $field ) ) { + } elseif ( ( $mode == LIST_SET ) && is_numeric( $field ) ) { $list .= "$value"; - } elseif ( ($mode == LIST_AND || $mode == LIST_OR) && is_array($value) ) { + } elseif ( ( $mode == LIST_AND || $mode == LIST_OR ) && is_array( $value ) ) { if ( count( $value ) == 0 ) { - throw new MWException( __METHOD__.': empty input' ); - } elseif( count( $value ) == 1 ) { + throw new MWException( __METHOD__ . ': empty input' ); + } elseif ( count( $value ) == 1 ) { // Special-case single values, as IN isn't terribly efficient // Don't necessarily assume the single key is 0; we don't // enforce linear numeric ordering on other arrays here. $value = array_values( $value ); - $list .= $field." = ".$this->addQuotes( $value[0] ); + $list .= $field . " = " . $this->addQuotes( $value[0] ); } else { - $list .= $field." IN (".$this->makeList($value).") "; + $list .= $field . " IN (" . $this->makeList( $value ) . ") "; } } elseif ( $value === null ) { if ( $mode == LIST_AND || $mode == LIST_OR ) { @@ -1181,6 +1238,7 @@ abstract class DatabaseBase implements DatabaseType { $list .= $mode == LIST_NAMES ? $value : $this->addQuotes( $value ); } } + return $list; } @@ -1195,11 +1253,12 @@ abstract class DatabaseBase implements DatabaseType { */ function makeWhereFrom2d( $data, $baseKey, $subKey ) { $conds = array(); + foreach ( $data as $base => $sub ) { if ( count( $sub ) ) { $conds[] = $this->makeList( array( $baseKey => $base, $subKey => array_keys( $sub ) ), - LIST_AND); + LIST_AND ); } } @@ -1215,28 +1274,28 @@ abstract class DatabaseBase implements DatabaseType { * Bitwise operations */ - function bitNot($field) { + function bitNot( $field ) { return "(~$field)"; } - function bitAnd($fieldLeft, $fieldRight) { + function bitAnd( $fieldLeft, $fieldRight ) { return "($fieldLeft & $fieldRight)"; } - function bitOr($fieldLeft, $fieldRight) { + function bitOr( $fieldLeft, $fieldRight ) { return "($fieldLeft | $fieldRight)"; } /** * Change the current database * + * @todo Explain what exactly will fail if this is not overridden. * @return bool Success or failure */ function selectDB( $db ) { # Stub. Shouldn't cause serious problems if it's not overridden, but # if your database engine supports a concept similar to MySQL's - # databases you may as well. TODO: explain what exactly will fail if - # this is not overridden. + # databases you may as well. return true; } @@ -1284,7 +1343,7 @@ abstract class DatabaseBase implements DatabaseType { # Note that we use a whitespace test rather than a \b test to avoid # any remote case where a word like on may be inside of a table name # surrounded by symbols which may be considered word breaks. - if( preg_match( '/(^|\s)(DISTINCT|JOIN|ON|AS)(\s|$)/i', $name ) !== 0 ) { + if ( preg_match( '/(^|\s)(DISTINCT|JOIN|ON|AS)(\s|$)/i', $name ) !== 0 ) { return $name; } @@ -1292,7 +1351,7 @@ abstract class DatabaseBase implements DatabaseType { # We reverse the explode so that database.table and table both output # the correct table. $dbDetails = array_reverse( explode( '.', $name, 2 ) ); - if( isset( $dbDetails[1] ) ) { + if ( isset( $dbDetails[1] ) ) { @list( $table, $database ) = $dbDetails; } else { @list( $table ) = $dbDetails; @@ -1301,13 +1360,13 @@ abstract class DatabaseBase implements DatabaseType { # A database name has been specified in input. Quote the table name # because we don't want any prefixes added. - if( isset($database) ) { + if ( isset( $database ) ) { $table = ( $table[0] == '`' ? $table : "`{$table}`" ); } # Note that we use the long format because php will complain in in_array if # the input is not an array, and will complain in is_array if it is not set. - if( !isset( $database ) # Don't use shared database if pre selected. + if ( !isset( $database ) # Don't use shared database if pre selected. && isset( $wgSharedDB ) # We have a shared database && $table[0] != '`' # Paranoia check to prevent shared tables listing '`table`' && isset( $wgSharedTables ) @@ -1318,15 +1377,14 @@ abstract class DatabaseBase implements DatabaseType { } # Quote the $database and $table and apply the prefix if not quoted. - if( isset($database) ) { + if ( isset( $database ) ) { $database = ( $database[0] == '`' ? $database : "`{$database}`" ); } $table = ( $table[0] == '`' ? $table : "`{$prefix}{$table}`" ); # Merge our database and table into our final table name. - $tableName = ( isset($database) ? "{$database}.{$table}" : "{$table}" ); + $tableName = ( isset( $database ) ? "{$database}.{$table}" : "{$table}" ); - # We're finished, return. return $tableName; } @@ -1342,9 +1400,11 @@ abstract class DatabaseBase implements DatabaseType { public function tableNames() { $inArray = func_get_args(); $retVal = array(); + foreach ( $inArray as $name ) { $retVal[$name] = $this->tableName( $name ); } + return $retVal; } @@ -1360,9 +1420,11 @@ abstract class DatabaseBase implements DatabaseType { public function tableNamesN() { $inArray = func_get_args(); $retVal = array(); + foreach ( $inArray as $name ) { $retVal[] = $this->tableName( $name ); } + return $retVal; } @@ -1372,41 +1434,48 @@ abstract class DatabaseBase implements DatabaseType { function tableNamesWithUseIndexOrJOIN( $tables, $use_index = array(), $join_conds = array() ) { $ret = array(); $retJOIN = array(); - $use_index_safe = is_array($use_index) ? $use_index : array(); - $join_conds_safe = is_array($join_conds) ? $join_conds : array(); + $use_index_safe = is_array( $use_index ) ? $use_index : array(); + $join_conds_safe = is_array( $join_conds ) ? $join_conds : array(); + foreach ( $tables as $table ) { // Is there a JOIN and INDEX clause for this table? - if ( isset($join_conds_safe[$table]) && isset($use_index_safe[$table]) ) { + if ( isset( $join_conds_safe[$table] ) && isset( $use_index_safe[$table] ) ) { $tableClause = $join_conds_safe[$table][0] . ' ' . $this->tableName( $table ); $tableClause .= ' ' . $this->useIndexClause( implode( ',', (array)$use_index_safe[$table] ) ); - $on = $this->makeList((array)$join_conds_safe[$table][1], LIST_AND); + $on = $this->makeList( (array)$join_conds_safe[$table][1], LIST_AND ); + if ( $on != '' ) { $tableClause .= ' ON (' . $on . ')'; } + $retJOIN[] = $tableClause; // Is there an INDEX clause? - } else if ( isset($use_index_safe[$table]) ) { + } else if ( isset( $use_index_safe[$table] ) ) { $tableClause = $this->tableName( $table ); $tableClause .= ' ' . $this->useIndexClause( implode( ',', (array)$use_index_safe[$table] ) ); $ret[] = $tableClause; // Is there a JOIN clause? - } else if ( isset($join_conds_safe[$table]) ) { + } else if ( isset( $join_conds_safe[$table] ) ) { $tableClause = $join_conds_safe[$table][0] . ' ' . $this->tableName( $table ); - $on = $this->makeList((array)$join_conds_safe[$table][1], LIST_AND); + $on = $this->makeList( (array)$join_conds_safe[$table][1], LIST_AND ); + if ( $on != '' ) { $tableClause .= ' ON (' . $on . ')'; } + $retJOIN[] = $tableClause; } else { $tableClause = $this->tableName( $table ); $ret[] = $tableClause; } } + // We can't separate explicit JOIN clauses with ',', use ' ' for those - $straightJoins = !empty($ret) ? implode( ',', $ret ) : ""; - $otherJoins = !empty($retJOIN) ? implode( ' ', $retJOIN ) : ""; + $straightJoins = !empty( $ret ) ? implode( ',', $ret ) : ""; + $otherJoins = !empty( $retJOIN ) ? implode( ' ', $retJOIN ) : ""; + // Compile our final table clause - return implode(' ',array($straightJoins,$otherJoins) ); + return implode( ' ', array( $straightJoins, $otherJoins ) ); } /** @@ -1419,6 +1488,7 @@ abstract class DatabaseBase implements DatabaseType { 'un_user_id' => 'user_id', 'un_user_ip' => 'user_ip', ); + if ( isset( $renamed[$index] ) ) { return $renamed[$index]; } else { @@ -1457,6 +1527,7 @@ abstract class DatabaseBase implements DatabaseType { $s = str_replace( '\\', '\\\\', $s ); $s = $this->strencode( $s ); $s = str_replace( array( '%', '_' ), array( '\%', '\_' ), $s ); + return $s; } @@ -1474,18 +1545,21 @@ abstract class DatabaseBase implements DatabaseType { */ function buildLike() { $params = func_get_args(); - if (count($params) > 0 && is_array($params[0])) { + + if ( count( $params ) > 0 && is_array( $params[0] ) ) { $params = $params[0]; } $s = ''; - foreach( $params as $value) { - if( $value instanceof LikeMatch ) { + + foreach ( $params as $value ) { + if ( $value instanceof LikeMatch ) { $s .= $value->toString(); } else { $s .= $this->escapeLikeInternal( $value ); } } + return " LIKE '" . $s . "' "; } @@ -1548,16 +1622,19 @@ abstract class DatabaseBase implements DatabaseType { $rows = array( $rows ); } - $sql = "REPLACE INTO $table (" . implode( ',', array_keys( $rows[0] ) ) .') VALUES '; + $sql = "REPLACE INTO $table (" . implode( ',', array_keys( $rows[0] ) ) . ') VALUES '; $first = true; + foreach ( $rows as $row ) { if ( $first ) { $first = false; } else { $sql .= ','; } + $sql .= '(' . $this->makeList( $row ) . ')'; } + return $this->query( $sql, $fname ); } @@ -1585,6 +1662,7 @@ abstract class DatabaseBase implements DatabaseType { $delTable = $this->tableName( $delTable ); $joinTable = $this->tableName( $joinTable ); $sql = "DELETE $delTable FROM $delTable, $joinTable WHERE $delVar=$joinVar "; + if ( $conds != '*' ) { $sql .= ' AND ' . $this->makeList( $conds, LIST_AND ); } @@ -1602,11 +1680,13 @@ abstract class DatabaseBase implements DatabaseType { $row = $this->fetchObject( $res ); $m = array(); + if ( preg_match( '/\((.*)\)/', $row->Type, $m ) ) { $size = $m[1]; } else { $size = -1; } + return $size; } @@ -1630,11 +1710,14 @@ abstract class DatabaseBase implements DatabaseType { if ( !$conds ) { throw new DBUnexpectedError( $this, 'DatabaseBase::delete() called with no conditions' ); } + $table = $this->tableName( $table ); $sql = "DELETE FROM $table"; + if ( $conds != '*' ) { $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND ); } + return $this->query( $sql, $fname ); } @@ -1649,25 +1732,33 @@ abstract class DatabaseBase implements DatabaseType { $insertOptions = array(), $selectOptions = array() ) { $destTable = $this->tableName( $destTable ); + if ( is_array( $insertOptions ) ) { $insertOptions = implode( ' ', $insertOptions ); } - if( !is_array( $selectOptions ) ) { + + if ( !is_array( $selectOptions ) ) { $selectOptions = array( $selectOptions ); } + list( $startOpts, $useIndex, $tailOpts ) = $this->makeSelectOptions( $selectOptions ); + if ( is_array( $srcTable ) ) { $srcTable = implode( ',', array_map( array( &$this, 'tableName' ), $srcTable ) ); } else { $srcTable = $this->tableName( $srcTable ); } + $sql = "INSERT $insertOptions INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' . " SELECT $startOpts " . implode( ',', $varMap ) . " FROM $srcTable $useIndex "; + if ( $conds != '*' ) { $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND ); } + $sql .= " $tailOpts"; + return $this->query( $sql, $fname ); } @@ -1689,14 +1780,16 @@ abstract class DatabaseBase implements DatabaseType { * @param $limit Integer: the SQL limit * @param $offset Integer the SQL offset (default false) */ - function limitResult( $sql, $limit, $offset=false ) { - if( !is_numeric( $limit ) ) { + function limitResult( $sql, $limit, $offset = false ) { + if ( !is_numeric( $limit ) ) { throw new DBUnexpectedError( $this, "Invalid non-numeric limit passed to limitResult()\n" ); } + return "$sql LIMIT " - . ( (is_numeric($offset) && $offset != 0) ? "{$offset}," : "" ) + . ( ( is_numeric( $offset ) && $offset != 0 ) ? "{$offset}," : "" ) . "{$limit} "; } + function limitResultForUpdate( $sql, $num ) { return $this->limitResult( $sql, $num, 0 ); } @@ -1718,9 +1811,9 @@ abstract class DatabaseBase implements DatabaseType { * @param $all Boolean: use UNION ALL * @return String: SQL fragment */ - function unionQueries($sqls, $all) { + function unionQueries( $sqls, $all ) { $glue = $all ? ') UNION ALL (' : ') UNION ('; - return '('.implode( $glue, $sqls ) . ')'; + return '(' . implode( $glue, $sqls ) . ')'; } /** @@ -1797,11 +1890,13 @@ abstract class DatabaseBase implements DatabaseType { $function = array_shift( $args ); $oldIgnore = $this->ignoreErrors( true ); $tries = DEADLOCK_TRIES; + if ( is_array( $function ) ) { $fname = $function[0]; } else { $fname = $function; } + do { $retVal = call_user_func_array( $function, $args ); $error = $this->lastError(); @@ -1817,7 +1912,9 @@ abstract class DatabaseBase implements DatabaseType { } } } while ( $this->wasDeadlock() && --$tries > 0 ); + $this->ignoreErrors( $oldIgnore ); + if ( $tries <= 0 ) { $this->rollback( $myFname ); $this->reportQueryError( $error, $errno, $sql, $fname ); @@ -1844,7 +1941,8 @@ abstract class DatabaseBase implements DatabaseType { } if ( !is_null( $this->mFakeSlaveLag ) ) { - $wait = intval( ( $pos->pos - microtime(true) + $this->mFakeSlaveLag ) * 1e6 ); + $wait = intval( ( $pos->pos - microtime( true ) + $this->mFakeSlaveLag ) * 1e6 ); + if ( $wait > $timeout * 1e6 ) { wfDebug( "Fake slave timed out waiting for $pos ($wait us)\n" ); wfProfileOut( $fname ); @@ -1866,6 +1964,7 @@ abstract class DatabaseBase implements DatabaseType { $encPos = intval( $pos->pos ); $sql = "SELECT MASTER_POS_WAIT($encFile, $encPos, $timeout)"; $res = $this->doQuery( $sql ); + if ( $res && $row = $this->fetchRow( $res ) ) { wfProfileOut( $fname ); return $row[0]; @@ -1880,14 +1979,16 @@ abstract class DatabaseBase implements DatabaseType { */ function getSlavePos() { if ( !is_null( $this->mFakeSlaveLag ) ) { - $pos = new MySQLMasterPos( 'fake', microtime(true) - $this->mFakeSlaveLag ); - wfDebug( __METHOD__.": fake slave pos = $pos\n" ); + $pos = new MySQLMasterPos( 'fake', microtime( true ) - $this->mFakeSlaveLag ); + wfDebug( __METHOD__ . ": fake slave pos = $pos\n" ); return $pos; } + $res = $this->query( 'SHOW SLAVE STATUS', 'DatabaseBase::getSlavePos' ); $row = $this->fetchObject( $res ); + if ( $row ) { - $pos = isset($row->Exec_master_log_pos) ? $row->Exec_master_log_pos : $row->Exec_Master_Log_Pos; + $pos = isset( $row->Exec_master_log_pos ) ? $row->Exec_master_log_pos : $row->Exec_Master_Log_Pos; return new MySQLMasterPos( $row->Relay_Master_Log_File, $pos ); } else { return false; @@ -1901,8 +2002,10 @@ abstract class DatabaseBase implements DatabaseType { if ( $this->mFakeMaster ) { return new MySQLMasterPos( 'fake', microtime( true ) ); } + $res = $this->query( 'SHOW MASTER STATUS', 'DatabaseBase::getMasterPos' ); $row = $this->fetchObject( $res ); + if ( $row ) { return new MySQLMasterPos( $row->File, $row->Position ); } else { @@ -1922,7 +2025,7 @@ abstract class DatabaseBase implements DatabaseType { * End a transaction */ function commit( $fname = 'DatabaseBase::commit' ) { - if( $this->mTrxLevel ) { + if ( $this->mTrxLevel ) { $this->query( 'COMMIT', $fname ); $this->mTrxLevel = 0; } @@ -1933,7 +2036,7 @@ abstract class DatabaseBase implements DatabaseType { * No-op on non-transactional databases. */ function rollback( $fname = 'DatabaseBase::rollback' ) { - if( $this->mTrxLevel ) { + if ( $this->mTrxLevel ) { $this->query( 'ROLLBACK', $fname, true ); $this->mTrxLevel = 0; } @@ -1976,8 +2079,8 @@ abstract class DatabaseBase implements DatabaseType { /** * Return MW-style timestamp used for MySQL schema */ - function timestamp( $ts=0 ) { - return wfTimestamp(TS_MW,$ts); + function timestamp( $ts = 0 ) { + return wfTimestamp( TS_MW, $ts ); } /** @@ -2010,7 +2113,7 @@ abstract class DatabaseBase implements DatabaseType { /** * Return aggregated value alias */ - function aggregateValue ($valuedata,$valuename='value') { + function aggregateValue ( $valuedata, $valuename = 'value' ) { return $valuename; } @@ -2036,12 +2139,14 @@ abstract class DatabaseBase implements DatabaseType { /** * Get status information from SHOW STATUS in an associative array */ - function getStatus($which="%") { + function getStatus( $which = "%" ) { $res = $this->query( "SHOW STATUS LIKE '{$which}'" ); $status = array(); + while ( $row = $this->fetchObject( $res ) ) { $status[$row->Variable_name] = $row->Value; } + return $status; } @@ -2052,11 +2157,11 @@ abstract class DatabaseBase implements DatabaseType { return 0; } - function encodeBlob($b) { + function encodeBlob( $b ) { return $b; } - function decodeBlob($b) { + function decodeBlob( $b ) { return $b; } @@ -2079,17 +2184,19 @@ abstract class DatabaseBase implements DatabaseType { */ function sourceFile( $filename, $lineCallback = false, $resultCallback = false ) { $fp = fopen( $filename, 'r' ); + if ( false === $fp ) { - if (!defined("MEDIAWIKI_INSTALL")) + if ( !defined( "MEDIAWIKI_INSTALL" ) ) throw new MWException( "Could not open \"{$filename}\".\n" ); else return "Could not open \"{$filename}\".\n"; } + try { $error = $this->sourceStream( $fp, $lineCallback, $resultCallback ); } - catch( MWException $e ) { - if ( defined("MEDIAWIKI_INSTALL") ) { + catch ( MWException $e ) { + if ( defined( "MEDIAWIKI_INSTALL" ) ) { $error = $e->getMessage(); } else { fclose( $fp ); @@ -2098,6 +2205,7 @@ abstract class DatabaseBase implements DatabaseType { } fclose( $fp ); + return $error; } @@ -2111,6 +2219,7 @@ abstract class DatabaseBase implements DatabaseType { */ public static function patchPath( $patch ) { global $wgDBtype, $IP; + if ( file_exists( "$IP/maintenance/$wgDBtype/archives/$patch" ) ) { return "$IP/maintenance/$wgDBtype/archives/$patch"; } else { @@ -2134,19 +2243,21 @@ abstract class DatabaseBase implements DatabaseType { if ( $lineCallback ) { call_user_func( $lineCallback ); } + $line = trim( fgets( $fp, 1024 ) ); $sl = strlen( $line ) - 1; if ( $sl < 0 ) { continue; } - if ( '-' == $line{0} && '-' == $line{1} ) { + + if ( '-' == $line { 0 } && '-' == $line { 1 } ) { continue; } - ## Allow dollar quoting for function declarations - if (substr($line,0,4) == '$mw$') { - if ($dollarquote) { + # # Allow dollar quoting for function declarations + if ( substr( $line, 0, 4 ) == '$mw$' ) { + if ( $dollarquote ) { $dollarquote = false; $done = true; } @@ -2154,8 +2265,8 @@ abstract class DatabaseBase implements DatabaseType { $dollarquote = true; } } - else if (!$dollarquote) { - if ( ';' == $line{$sl} && ($sl < 2 || ';' != $line{$sl - 1})) { + else if ( !$dollarquote ) { + if ( ';' == $line { $sl } && ( $sl < 2 || ';' != $line { $sl - 1 } ) ) { $done = true; $line = substr( $line, 0, $sl ); } @@ -2164,12 +2275,14 @@ abstract class DatabaseBase implements DatabaseType { if ( $cmd != '' ) { $cmd .= ' '; } + $cmd .= "$line\n"; if ( $done ) { - $cmd = str_replace(';;', ";", $cmd); + $cmd = str_replace( ';;', ";", $cmd ); $cmd = $this->replaceVars( $cmd ); $res = $this->query( $cmd, __METHOD__ ); + if ( $resultCallback ) { call_user_func( $resultCallback, $res, $this ); } @@ -2183,10 +2296,10 @@ abstract class DatabaseBase implements DatabaseType { $done = false; } } + return true; } - /** * Replace variables in sourced SQL */ @@ -2214,6 +2327,7 @@ abstract class DatabaseBase implements DatabaseType { // Index names $ins = preg_replace_callback( '!/\*i\*/([a-zA-Z_0-9]*)!', array( $this, 'indexNameCallback' ), $ins ); + return $ins; } @@ -2314,7 +2428,6 @@ abstract class DatabaseBase implements DatabaseType { } } - /****************************************************************************** * Utility classes *****************************************************************************/ @@ -2326,7 +2439,7 @@ abstract class DatabaseBase implements DatabaseType { class DBObject { public $mData; - function __construct($data) { + function __construct( $data ) { $this->mData = $data; } @@ -2347,9 +2460,11 @@ class DBObject { */ class Blob { private $mData; - function __construct($data) { + + function __construct( $data ) { $this->mData = $data; } + function fetch() { return $this->mData; } @@ -2362,7 +2477,8 @@ class Blob { class MySQLField { private $name, $tablename, $default, $max_length, $nullable, $is_pk, $is_unique, $is_multiple, $is_key, $type; - function __construct ($info) { + + function __construct ( $info ) { $this->name = $info->name; $this->tablename = $info->table; $this->default = $info->def; @@ -2371,7 +2487,7 @@ class MySQLField { $this->is_pk = $info->primary_key; $this->is_unique = $info->unique_key; $this->is_multiple = $info->multiple_key; - $this->is_key = ($this->is_pk || $this->is_unique || $this->is_multiple); + $this->is_key = ( $this->is_pk || $this->is_unique || $this->is_multiple ); $this->type = $info->type; } @@ -2431,10 +2547,13 @@ class DBError extends MWException { function getText() { global $wgShowDBErrorBacktrace; + $s = $this->getMessage() . "\n"; + if ( $wgShowDBErrorBacktrace ) { $s .= "Backtrace:\n" . $this->getTraceAsString() . "\n"; } + return $s; } } @@ -2447,10 +2566,13 @@ class DBConnectionError extends DBError { function __construct( DatabaseBase &$db, $error = 'unknown error' ) { $msg = 'DB connection error'; + if ( trim( $error ) != '' ) { $msg .= ": $error"; } + $this->error = $error; + parent::__construct( $db, $msg ); } @@ -2471,7 +2593,9 @@ class DBConnectionError extends DBError { function getPageTitle() { global $wgSitename, $wgLang; + $header = "$wgSitename has a problem"; + if ( $wgLang instanceof Language ) { $header = htmlspecialchars( $wgLang->getMessage( 'dberr-header' ) ); } @@ -2498,7 +2622,7 @@ class DBConnectionError extends DBError { } if ( trim( $this->error ) == '' ) { - $this->error = $this->db->getProperty('mServer'); + $this->error = $this->db->getProperty( 'mServer' ); } $noconnect = "

    $sorry
    $again

    $info

    "; @@ -2510,33 +2634,38 @@ class DBConnectionError extends DBError { $extra = $this->searchForm(); - if( $wgUseFileCache ) { + if ( $wgUseFileCache ) { try { $cache = $this->fileCachedPage(); # Cached version on file system? - if( $cache !== null ) { + if ( $cache !== null ) { # Hack: extend the body for error messages - $cache = str_replace( array('',''), '', $cache ); + $cache = str_replace( array( '', '' ), '', $cache ); # Add cache notice... $cachederror = "This is a cached copy of the requested page, and may not be up to date. "; + # Localize it if possible... - if( $wgLang instanceof Language ) { + if ( $wgLang instanceof Language ) { $cachederror = htmlspecialchars( $wgLang->getMessage( 'dberr-cachederror' ) ); } + $warning = "
    $cachederror
    "; + # Output cached page with notices on bottom and re-close body return "{$cache}{$warning}
    $text
    $extra"; } - } catch( MWException $e ) { + } catch ( MWException $e ) { // Do nothing, just use the default page } } + # Headers needed here - output is just the error message - return $this->htmlHeader()."$text
    $extra".$this->htmlFooter(); + return $this->htmlHeader() . "$text
    $extra" . $this->htmlFooter(); } function searchForm() { global $wgSitename, $wgServer, $wgLang, $wgInputEncoding; + $usegoogle = "You can try searching via Google in the meantime."; $outofdate = "Note that their indexes of our content may be out of date."; $googlesearch = "Search"; @@ -2547,7 +2676,7 @@ class DBConnectionError extends DBError { $googlesearch = htmlspecialchars( $wgLang->getMessage( 'searchbutton' ) ); } - $search = htmlspecialchars(@$_REQUEST['search']); + $search = htmlspecialchars( @$_REQUEST['search'] ); $trygoogle = <<$usegoogle
    @@ -2573,22 +2702,27 @@ EOT; function fileCachedPage() { global $wgTitle, $title, $wgLang, $wgOut; - if( $wgOut->isDisabled() ) return; // Done already? + + if ( $wgOut->isDisabled() ) { + return; // Done already? + } + $mainpage = 'Main Page'; + if ( $wgLang instanceof Language ) { $mainpage = htmlspecialchars( $wgLang->getMessage( 'mainpage' ) ); } - if( $wgTitle ) { + if ( $wgTitle ) { $t =& $wgTitle; - } elseif( $title ) { + } elseif ( $title ) { $t = Title::newFromURL( $title ); } else { $t = Title::newFromText( $mainpage ); } $cache = new HTMLFileCache( $t ); - if( $cache->isFileCached() ) { + if ( $cache->isFileCached() ) { return $cache->fetchPageText(); } else { return ''; @@ -2598,7 +2732,6 @@ EOT; function htmlBodyOnly() { return true; } - } /** @@ -2614,6 +2747,7 @@ class DBQueryError extends DBError { "Error: $errno $error\n"; parent::__construct( $db, $message ); + $this->error = $error; $this->errno = $errno; $this->sql = $sql; @@ -2622,12 +2756,15 @@ class DBQueryError extends DBError { function getText() { global $wgShowDBErrorBacktrace; + if ( $this->useMessageCache() ) { $s = wfMsg( 'dberrortextcl', htmlspecialchars( $this->getSQL() ), htmlspecialchars( $this->fname ), $this->errno, htmlspecialchars( $this->error ) ) . "\n"; + if ( $wgShowDBErrorBacktrace ) { $s .= "Backtrace:\n" . $this->getTraceAsString() . "\n"; } + return $s; } else { return parent::getText(); @@ -2636,7 +2773,8 @@ class DBQueryError extends DBError { function getSQL() { global $wgShowSQLErrors; - if( !$wgShowSQLErrors ) { + + if ( !$wgShowSQLErrors ) { return $this->msg( 'sqlhidden', 'SQL hidden' ); } else { return $this->sql; @@ -2654,15 +2792,18 @@ class DBQueryError extends DBError { function getHTML() { global $wgShowDBErrorBacktrace; + if ( $this->useMessageCache() ) { $s = wfMsgNoDB( 'dberrortext', htmlspecialchars( $this->getSQL() ), htmlspecialchars( $this->fname ), $this->errno, htmlspecialchars( $this->error ) ); } else { $s = nl2br( htmlspecialchars( $this->getMessage() ) ); } + if ( $wgShowDBErrorBacktrace ) { $s .= '

    Backtrace:

    ' . nl2br( htmlspecialchars( $this->getTraceAsString() ) ); } + return $s; } } @@ -2685,6 +2826,7 @@ class ResultWrapper implements Iterator { */ function __construct( $database, $result ) { $this->db = $database; + if ( $result instanceof ResultWrapper ) { $this->result = $result->result; } else { @@ -2746,8 +2888,8 @@ class ResultWrapper implements Iterator { */ function rewind() { - if ($this->numRows()) { - $this->db->dataSeek($this, 0); + if ( $this->numRows() ) { + $this->db->dataSeek( $this, 0 ); } $this->pos = 0; $this->currentRow = null; @@ -2775,17 +2917,17 @@ class ResultWrapper implements Iterator { } } -/* Overloads the relevant methods of the real ResultsWrapper so it +/** + * Overloads the relevant methods of the real ResultsWrapper so it * doesn't go anywhere near an actual database. */ class FakeResultWrapper extends ResultWrapper { - var $result = array(); var $db = null; // And it's going to stay that way :D var $pos = 0; var $currentRow = null; - function __construct( $array ){ + function __construct( $array ) { $this->result = $array; } @@ -2805,7 +2947,7 @@ class FakeResultWrapper extends ResultWrapper { function free() {} // Callers want to be able to access fields with $this->fieldName - function fetchObject(){ + function fetchObject() { $this->currentRow = $this->result[$this->pos++]; return (object)$this->currentRow; } diff --git a/includes/filerepo/LocalFile.php b/includes/filerepo/LocalFile.php index 22fb347378..67ab6e9d75 100644 --- a/includes/filerepo/LocalFile.php +++ b/includes/filerepo/LocalFile.php @@ -71,6 +71,7 @@ class LocalFile extends File { $title = Title::makeTitle( NS_FILE, $row->img_name ); $file = new self( $title, $repo ); $file->loadFromRow( $row ); + return $file; } @@ -80,12 +81,15 @@ class LocalFile extends File { */ static function newFromKey( $sha1, $repo, $timestamp = false ) { $conds = array( 'img_sha1' => $sha1 ); - if( $timestamp ) { + + if ( $timestamp ) { $conds['img_timestamp'] = $timestamp; } + $dbr = $repo->getSlaveDB(); $row = $dbr->selectRow( 'image', self::selectFields(), $conds, __METHOD__ ); - if( $row ) { + + if ( $row ) { return self::newFromRow( $row, $repo ); } else { return false; @@ -119,10 +123,12 @@ class LocalFile extends File { * Do not call this except from inside a repo class. */ function __construct( $title, $repo ) { - if( !is_object( $title ) ) { + if ( !is_object( $title ) ) { throw new MWException( __CLASS__ . ' constructor given bogus title.' ); } + parent::__construct( $title, $repo ); + $this->metadata = ''; $this->historyLine = 0; $this->historyRes = null; @@ -135,6 +141,7 @@ class LocalFile extends File { */ function getCacheKey() { $hashedName = md5( $this->getName() ); + return $this->repo->getSharedCacheKey( 'file', $hashedName ); } @@ -143,13 +150,16 @@ class LocalFile extends File { */ function loadFromCache() { global $wgMemc; + wfProfileIn( __METHOD__ ); $this->dataLoaded = false; $key = $this->getCacheKey(); + if ( !$key ) { wfProfileOut( __METHOD__ ); return false; } + $cachedValues = $wgMemc->get( $key ); // Check if the key existed and belongs to this version of MediaWiki @@ -161,6 +171,7 @@ class LocalFile extends File { } $this->dataLoaded = true; } + if ( $this->dataLoaded ) { wfIncrStats( 'image_cache_hit' ); } else { @@ -176,14 +187,18 @@ class LocalFile extends File { */ function saveToCache() { global $wgMemc; + $this->load(); $key = $this->getCacheKey(); + if ( !$key ) { return; } + $fields = $this->getCacheFields( '' ); $cache = array( 'version' => MW_FILE_VERSION ); $cache['fileExists'] = $this->fileExists; + if ( $this->fileExists ) { foreach ( $fields as $field ) { $cache[$field] = $this->$field; @@ -204,9 +219,11 @@ class LocalFile extends File { static $fields = array( 'size', 'width', 'height', 'bits', 'media_type', 'major_mime', 'minor_mime', 'metadata', 'timestamp', 'sha1', 'user', 'user_text', 'description' ); static $results = array(); + if ( $prefix == '' ) { return $fields; } + if ( !isset( $results[$prefix] ) ) { $prefixedFields = array(); foreach ( $fields as $field ) { @@ -214,6 +231,7 @@ class LocalFile extends File { } $results[$prefix] = $prefixedFields; } + return $results[$prefix]; } @@ -232,6 +250,7 @@ class LocalFile extends File { $row = $dbr->selectRow( 'image', $this->getCacheFields( 'img_' ), array( 'img_name' => $this->getName() ), $fname ); + if ( $row ) { $this->loadFromRow( $row ); } else { @@ -248,15 +267,20 @@ class LocalFile extends File { function decodeRow( $row, $prefix = 'img_' ) { $array = (array)$row; $prefixLength = strlen( $prefix ); + // Sanity check prefix once if ( substr( key( $array ), 0, $prefixLength ) !== $prefix ) { throw new MWException( __METHOD__ . ': incorrect $prefix parameter' ); } + $decoded = array(); + foreach ( $array as $name => $value ) { $decoded[substr( $name, $prefixLength )] = $value; } + $decoded['timestamp'] = wfTimestamp( TS_MW, $decoded['timestamp'] ); + if ( empty( $decoded['major_mime'] ) ) { $decoded['mime'] = 'unknown/unknown'; } else { @@ -265,8 +289,10 @@ class LocalFile extends File { } $decoded['mime'] = $decoded['major_mime'] . '/' . $decoded['minor_mime']; } + # Trim zero padding from char/binary field $decoded['sha1'] = rtrim( $decoded['sha1'], "\0" ); + return $decoded; } @@ -276,9 +302,11 @@ class LocalFile extends File { function loadFromRow( $row, $prefix = 'img_' ) { $this->dataLoaded = true; $array = $this->decodeRow( $row, $prefix ); + foreach ( $array as $name => $value ) { $this->$name = $value; } + $this->fileExists = true; $this->maybeUpgradeRow(); } @@ -303,6 +331,7 @@ class LocalFile extends File { if ( wfReadOnly() ) { return; } + if ( is_null( $this->media_type ) || $this->mime == 'image/svg' ) { @@ -335,6 +364,7 @@ class LocalFile extends File { wfProfileOut( __METHOD__ ); return; } + $dbw = $this->repo->getMasterDB(); list( $major, $minor ) = self::splitMime( $this->mime ); @@ -357,6 +387,7 @@ class LocalFile extends File { ), array( 'img_name' => $this->getName() ), __METHOD__ ); + $this->saveToCache(); wfProfileOut( __METHOD__ ); } @@ -372,6 +403,7 @@ class LocalFile extends File { $this->dataLoaded = true; $fields = $this->getCacheFields( '' ); $fields[] = 'fileExists'; + foreach ( $fields as $field ) { if ( isset( $info[$field] ) ) { $this->$field = $info[$field]; @@ -396,7 +428,7 @@ class LocalFile extends File { /** isVisible inhereted */ function isMissing() { - if( $this->missing === null ) { + if ( $this->missing === null ) { list( $fileExists ) = $this->repo->fileExistsBatch( array( $this->getVirtualUrl() ), FileRepo::FILES_ONLY ); $this->missing = !$fileExists; } @@ -410,6 +442,7 @@ class LocalFile extends File { */ public function getWidth( $page = 1 ) { $this->load(); + if ( $this->isMultipage() ) { $dim = $this->getHandler()->getPageDimensions( $this, $page ); if ( $dim ) { @@ -429,6 +462,7 @@ class LocalFile extends File { */ public function getHeight( $page = 1 ) { $this->load(); + if ( $this->isMultipage() ) { $dim = $this->getHandler()->getPageDimensions( $this, $page ); if ( $dim ) { @@ -448,9 +482,10 @@ class LocalFile extends File { */ function getUser( $type = 'text' ) { $this->load(); - if( $type == 'text' ) { + + if ( $type == 'text' ) { return $this->user_text; - } elseif( $type == 'id' ) { + } elseif ( $type == 'id' ) { return $this->user; } } @@ -521,6 +556,7 @@ class LocalFile extends File { function migrateThumbFile( $thumbName ) { $thumbDir = $this->getThumbPath(); $thumbPath = "$thumbDir/$thumbName"; + if ( is_dir( $thumbPath ) ) { // Directory where file should be // This happened occasionally due to broken migration code in 1.5 @@ -535,6 +571,7 @@ class LocalFile extends File { // Doesn't exist anymore clearstatcache(); } + if ( is_file( $thumbDir ) ) { // File where directory should be unlink( $thumbDir ); @@ -552,6 +589,7 @@ class LocalFile extends File { */ function getThumbnails() { $this->load(); + $files = array(); $dir = $this->getThumbPath(); @@ -560,10 +598,11 @@ class LocalFile extends File { if ( $handle ) { while ( false !== ( $file = readdir( $handle ) ) ) { - if ( $file{0} != '.' ) { + if ( $file { 0 } != '.' ) { $files[] = $file; } } + closedir( $handle ); } } @@ -585,8 +624,10 @@ class LocalFile extends File { */ function purgeHistory() { global $wgMemc; + $hashedName = md5( $this->getName() ); $oldKey = $this->repo->getSharedCacheKey( 'oldfile', $hashedName ); + if ( $oldKey ) { $wgMemc->delete( $oldKey ); } @@ -611,10 +652,12 @@ class LocalFile extends File { */ function purgeThumbnails() { global $wgUseSquid; + // Delete thumbnails $files = $this->getThumbnails(); $dir = $this->getThumbPath(); $urls = array(); + foreach ( $files as $file ) { # Check that the base file name is part of the thumb name # This is a basic sanity check to avoid erasing unrelated directories @@ -641,15 +684,19 @@ class LocalFile extends File { $conds = $opts = $join_conds = array(); $eq = $inc ? '=' : ''; $conds[] = "oi_name = " . $dbr->addQuotes( $this->title->getDBkey() ); - if( $start ) { + + if ( $start ) { $conds[] = "oi_timestamp <$eq " . $dbr->addQuotes( $dbr->timestamp( $start ) ); } - if( $end ) { + + if ( $end ) { $conds[] = "oi_timestamp >$eq " . $dbr->addQuotes( $dbr->timestamp( $end ) ); } - if( $limit ) { + + if ( $limit ) { $opts['LIMIT'] = $limit; } + // Search backwards for time > x queries $order = ( !$start && $end !== null ) ? 'ASC' : 'DESC'; $opts['ORDER BY'] = "oi_timestamp $order"; @@ -660,16 +707,19 @@ class LocalFile extends File { $res = $dbr->select( $tables, $fields, $conds, __METHOD__, $opts, $join_conds ); $r = array(); - while( $row = $dbr->fetchObject( $res ) ) { + + while ( $row = $dbr->fetchObject( $res ) ) { if ( $this->repo->oldFileFromRowFactory ) { $r[] = call_user_func( $this->repo->oldFileFromRowFactory, $row, $this->repo ); } else { $r[] = OldLocalFile::newFromRow( $row, $this->repo ); } } - if( $order == 'ASC' ) { + + if ( $order == 'ASC' ) { $r = array_reverse( $r ); // make sure it ends up descending } + return $r; } @@ -698,6 +748,7 @@ class LocalFile extends File { array( 'img_name' => $this->title->getDBkey() ), $fname ); + if ( 0 == $dbr->numRows( $this->historyRes ) ) { $this->historyRes = null; return false; @@ -719,6 +770,7 @@ class LocalFile extends File { */ public function resetHistory() { $this->historyLine = 0; + if ( !is_null( $this->historyRes ) ) { $this->historyRes = null; } @@ -757,12 +809,15 @@ class LocalFile extends File { function upload( $srcPath, $comment, $pageText, $flags = 0, $props = false, $timestamp = false, $user = null ) { $this->lock(); $status = $this->publish( $srcPath, $flags ); + if ( $status->ok ) { if ( !$this->recordUpload2( $status->value, $comment, $pageText, $props, $timestamp, $user ) ) { $status->fatal( 'filenotfound', $srcPath ); } } + $this->unlock(); + return $status; } @@ -774,9 +829,11 @@ class LocalFile extends File { $watch = false, $timestamp = false ) { $pageText = SpecialUpload::getInitialPageText( $desc, $license, $copyStatus, $source ); + if ( !$this->recordUpload2( $oldver, $desc, $pageText ) ) { return false; } + if ( $watch ) { global $wgUser; $wgUser->addWatch( $this->getTitle() ); @@ -790,7 +847,7 @@ class LocalFile extends File { */ function recordUpload2( $oldver, $comment, $pageText, $props = false, $timestamp = false, $user = null ) { - if( is_null( $user ) ) { + if ( is_null( $user ) ) { global $wgUser; $user = $wgUser; } @@ -801,6 +858,7 @@ class LocalFile extends File { if ( !$props ) { $props = $this->repo->getFileProps( $this->getVirtualUrl() ); } + $props['description'] = $comment; $props['user'] = $user->getId(); $props['user_text'] = $user->getName(); @@ -809,6 +867,7 @@ class LocalFile extends File { # Delete thumbnails $this->purgeThumbnails(); + # The file is already on its final location, remove it from the squid cache SquidUpdate::purge( array( $this->getURL() ) ); @@ -819,6 +878,7 @@ class LocalFile extends File { } $reupload = false; + if ( $timestamp === false ) { $timestamp = $dbw->timestamp(); } @@ -829,7 +889,7 @@ class LocalFile extends File { $dbw->insert( 'image', array( 'img_name' => $this->getName(), - 'img_size'=> $this->size, + 'img_size' => $this->size, 'img_width' => intval( $this->width ), 'img_height' => intval( $this->height ), 'img_bits' => $this->bits, @@ -847,7 +907,7 @@ class LocalFile extends File { 'IGNORE' ); - if( $dbw->affectedRows() == 0 ) { + if ( $dbw->affectedRows() == 0 ) { $reupload = true; # Collision, this is an update of a file @@ -908,11 +968,15 @@ class LocalFile extends File { $action = $reupload ? 'overwrite' : 'upload'; $log->addEntry( $action, $descTitle, $comment, array(), $user ); - if( $descTitle->exists() ) { + if ( $descTitle->exists() ) { # Create a null revision $latest = $descTitle->getLatestRevID(); - $nullRevision = Revision::newNullRevision( $dbw, $descTitle->getArticleId(), - $log->getRcComment(), false ); + $nullRevision = Revision::newNullRevision( + $dbw, + $descTitle->getArticleId(), + $log->getRcComment(), + false + ); $nullRevision->insertOn( $dbw ); wfRunHooks( 'NewRevisionFromEditComplete', array( $article, $nullRevision, $latest, $user ) ); @@ -937,16 +1001,18 @@ class LocalFile extends File { # in case of a rollback there is an usable file from memcached # which in fact doesn't really exist (bug 24978) $this->saveToCache(); - + # Hooks, hooks, the magic of hooks... wfRunHooks( 'FileUpload', array( $this, $reupload, $descTitle->exists() ) ); # Invalidate cache for all pages using this file $update = new HTMLCacheUpdate( $this->getTitle(), 'imagelinks' ); $update->doUpdate(); + # Invalidate cache for all pages that redirects on this page $redirs = $this->getTitle()->getRedirectsHere(); - foreach( $redirs as $redir ) { + + foreach ( $redirs as $redir ) { $update = new HTMLCacheUpdate( $redir, 'imagelinks' ); $update->doUpdate(); } @@ -971,17 +1037,21 @@ class LocalFile extends File { */ function publish( $srcPath, $flags = 0 ) { $this->lock(); + $dstRel = $this->getRel(); - $archiveName = gmdate( 'YmdHis' ) . '!'. $this->getName(); + $archiveName = gmdate( 'YmdHis' ) . '!' . $this->getName(); $archiveRel = 'archive/' . $this->getHashPath() . $archiveName; $flags = $flags & File::DELETE_SOURCE ? LocalRepo::DELETE_SOURCE : 0; $status = $this->repo->publish( $srcPath, $dstRel, $archiveRel, $flags ); + if ( $status->value == 'new' ) { $status->value = ''; } else { $status->value = $archiveName; } + $this->unlock(); + return $status; } @@ -1005,12 +1075,14 @@ class LocalFile extends File { function move( $target ) { wfDebugLog( 'imagemove', "Got request to move {$this->name} to " . $target->getText() ); $this->lock(); + $batch = new LocalFileMoveBatch( $this, $target ); $batch->addCurrent(); $batch->addOlds(); $status = $batch->execute(); wfDebugLog( 'imagemove', "Finished moving {$this->name}" ); + $this->purgeEverything(); $this->unlock(); @@ -1041,6 +1113,7 @@ class LocalFile extends File { */ function delete( $reason, $suppress = false ) { $this->lock(); + $batch = new LocalFileDeleteBatch( $this, $reason, $suppress ); $batch->addCurrent(); @@ -1062,6 +1135,7 @@ class LocalFile extends File { } $this->unlock(); + return $status; } @@ -1079,16 +1153,20 @@ class LocalFile extends File { * @throws MWException or FSException on database or file store failure * @return FileRepoStatus object. */ - function deleteOld( $archiveName, $reason, $suppress=false ) { + function deleteOld( $archiveName, $reason, $suppress = false ) { $this->lock(); + $batch = new LocalFileDeleteBatch( $this, $reason, $suppress ); $batch->addOld( $archiveName ); $status = $batch->execute(); + $this->unlock(); + if ( $status->ok ) { $this->purgeDescription(); $this->purgeHistory(); } + return $status; } @@ -1105,12 +1183,15 @@ class LocalFile extends File { */ function restore( $versions = array(), $unsuppress = false ) { $batch = new LocalFileRestoreBatch( $this, $unsuppress ); + if ( !$versions ) { $batch->addAll(); } else { $batch->addIds( $versions ); } + $status = $batch->execute(); + if ( !$status->ok ) { return $status; } @@ -1119,6 +1200,7 @@ class LocalFile extends File { $cleanupStatus->successCount = 0; $cleanupStatus->failCount = 0; $status->merge( $cleanupStatus ); + return $status; } @@ -1165,10 +1247,12 @@ class LocalFile extends File { */ function lock() { $dbw = $this->repo->getMasterDB(); + if ( !$this->locked ) { $dbw->begin(); $this->locked++; } + return $dbw->selectField( 'image', '1', array( 'img_name' => $this->getName() ), __METHOD__ ); } @@ -1196,7 +1280,7 @@ class LocalFile extends File { } } // LocalFile class -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ /** * Helper class for file deletion @@ -1231,25 +1315,33 @@ class LocalFileDeleteBatch { unset( $oldRels['.'] ); $deleteCurrent = true; } + return array( $oldRels, $deleteCurrent ); } /*protected*/ function getHashes() { $hashes = array(); list( $oldRels, $deleteCurrent ) = $this->getOldRels(); + if ( $deleteCurrent ) { $hashes['.'] = $this->file->getSha1(); } + if ( count( $oldRels ) ) { $dbw = $this->file->repo->getMasterDB(); - $res = $dbw->select( 'oldimage', array( 'oi_archive_name', 'oi_sha1' ), - 'oi_archive_name IN(' . $dbw->makeList( array_keys( $oldRels ) ) . ')', - __METHOD__ ); + $res = $dbw->select( + 'oldimage', + array( 'oi_archive_name', 'oi_sha1' ), + 'oi_archive_name IN (' . $dbw->makeList( array_keys( $oldRels ) ) . ')', + __METHOD__ + ); + while ( $row = $dbw->fetchObject( $res ) ) { if ( rtrim( $row->oi_sha1, "\0" ) === '' ) { // Get the hash from the file $oldUrl = $this->file->getArchiveVirtualUrl( $row->oi_archive_name ); $props = $this->file->repo->getFileProps( $oldUrl ); + if ( $props['fileExists'] ) { // Upgrade the oldimage row $dbw->update( 'oldimage', @@ -1265,10 +1357,13 @@ class LocalFileDeleteBatch { } } } + $missing = array_diff_key( $this->srcRels, $hashes ); + foreach ( $missing as $name => $rel ) { $this->status->error( 'filedelete-old-unregistered', $name ); } + foreach ( $hashes as $name => $hash ) { if ( !$hash ) { $this->status->error( 'filedelete-missing', $this->srcRels[$name] ); @@ -1281,6 +1376,7 @@ class LocalFileDeleteBatch { function doDBInserts() { global $wgUser; + $dbw = $this->file->repo->getMasterDB(); $encTimestamp = $dbw->addQuotes( $dbw->timestamp() ); $encUserId = $dbw->addQuotes( $wgUser->getId() ); @@ -1368,6 +1464,7 @@ class LocalFileDeleteBatch { function doDBDeletes() { $dbw = $this->file->repo->getMasterDB(); list( $oldRels, $deleteCurrent ) = $this->getOldRels(); + if ( count( $oldRels ) ) { $dbw->delete( 'oldimage', array( @@ -1375,6 +1472,7 @@ class LocalFileDeleteBatch { 'oi_archive_name' => array_keys( $oldRels ) ), __METHOD__ ); } + if ( $deleteCurrent ) { $dbw->delete( 'image', array( 'img_name' => $this->file->getName() ), __METHOD__ ); } @@ -1392,14 +1490,16 @@ class LocalFileDeleteBatch { $privateFiles = array(); list( $oldRels, $deleteCurrent ) = $this->getOldRels(); $dbw = $this->file->repo->getMasterDB(); - if( !empty( $oldRels ) ) { + + if ( !empty( $oldRels ) ) { $res = $dbw->select( 'oldimage', array( 'oi_archive_name' ), array( 'oi_name' => $this->file->getName(), - 'oi_archive_name IN (' . $dbw->makeList( array_keys($oldRels) ) . ')', - $dbw->bitAnd('oi_deleted', File::DELETED_FILE) => File::DELETED_FILE ), + 'oi_archive_name IN (' . $dbw->makeList( array_keys( $oldRels ) ) . ')', + $dbw->bitAnd( 'oi_deleted', File::DELETED_FILE ) => File::DELETED_FILE ), __METHOD__ ); - while( $row = $dbw->fetchObject( $res ) ) { + + while ( $row = $dbw->fetchObject( $res ) ) { $privateFiles[$row->oi_archive_name] = 1; } } @@ -1408,6 +1508,7 @@ class LocalFileDeleteBatch { $this->deletionBatch = array(); $ext = $this->file->getExtension(); $dotExt = $ext === '' ? '' : ".$ext"; + foreach ( $this->srcRels as $name => $srcRel ) { // Skip files that have no hash (missing source). // Keep private files where they are. @@ -1432,6 +1533,7 @@ class LocalFileDeleteBatch { // Execute the file deletion batch $status = $this->file->repo->deleteBatch( $this->deletionBatch ); + if ( !$status->isGood() ) { $this->status->merge( $status ); } @@ -1448,6 +1550,7 @@ class LocalFileDeleteBatch { // Purge squid if ( $wgUseSquid ) { $urls = array(); + foreach ( $this->srcRels as $srcRel ) { $urlRel = str_replace( '%2F', '/', rawurlencode( $srcRel ) ); $urls[] = $this->file->repo->getZoneUrl( 'public' ) . '/' . $urlRel; @@ -1461,6 +1564,7 @@ class LocalFileDeleteBatch { // Commit and return $this->file->unlock(); wfProfileOut( __METHOD__ ); + return $this->status; } @@ -1469,19 +1573,25 @@ class LocalFileDeleteBatch { */ function removeNonexistentFiles( $batch ) { $files = $newBatch = array(); - foreach( $batch as $batchItem ) { + + foreach ( $batch as $batchItem ) { list( $src, $dest ) = $batchItem; $files[$src] = $this->file->repo->getVirtualUrl( 'public' ) . '/' . rawurlencode( $src ); } + $result = $this->file->repo->fileExistsBatch( $files, FSRepo::FILES_ONLY ); - foreach( $batch as $batchItem ) - if( $result[$batchItem[0]] ) + + foreach ( $batch as $batchItem ) { + if ( $result[$batchItem[0]] ) { $newBatch[] = $batchItem; + } + } + return $newBatch; } } -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ /** * Helper class for file undeletion @@ -1527,6 +1637,7 @@ class LocalFileRestoreBatch { */ function execute() { global $wgLang; + if ( !$this->all && !$this->ids ) { // Do nothing return $this->file->repo->newGood(); @@ -1539,7 +1650,8 @@ class LocalFileRestoreBatch { // Fetch all or selected archived revisions for the file, // sorted from the most recent to the oldest. $conditions = array( 'fa_name' => $this->file->getName() ); - if( !$this->all ) { + + if ( !$this->all ) { $conditions[] = 'fa_id IN (' . $dbw->makeList( $this->ids ) . ')'; } @@ -1556,7 +1668,8 @@ class LocalFileRestoreBatch { $deleteIds = array(); $first = true; $archiveNames = array(); - while( $row = $dbw->fetchObject( $result ) ) { + + while ( $row = $dbw->fetchObject( $result ) ) { $idsPresent[] = $row->fa_id; if ( $row->fa_name != $this->file->getName() ) { @@ -1564,6 +1677,7 @@ class LocalFileRestoreBatch { $status->failCount++; continue; } + if ( $row->fa_storage_key == '' ) { // Revision was missing pre-deletion $status->error( 'undelete-bad-store-key', $wgLang->timeanddate( $row->fa_timestamp ) ); @@ -1575,12 +1689,13 @@ class LocalFileRestoreBatch { $deletedUrl = $this->file->repo->getVirtualUrl() . '/deleted/' . $deletedRel; $sha1 = substr( $row->fa_storage_key, 0, strcspn( $row->fa_storage_key, '.' ) ); + # Fix leading zero if ( strlen( $sha1 ) == 32 && $sha1[0] == '0' ) { $sha1 = substr( $sha1, 1 ); } - if( is_null( $row->fa_major_mime ) || $row->fa_major_mime == 'unknown' + if ( is_null( $row->fa_major_mime ) || $row->fa_major_mime == 'unknown' || is_null( $row->fa_minor_mime ) || $row->fa_minor_mime == 'unknown' || is_null( $row->fa_media_type ) || $row->fa_media_type == 'UNKNOWN' || is_null( $row->fa_metadata ) ) { @@ -1615,23 +1730,27 @@ class LocalFileRestoreBatch { 'img_timestamp' => $row->fa_timestamp, 'img_sha1' => $sha1 ); + // The live (current) version cannot be hidden! - if( !$this->unsuppress && $row->fa_deleted ) { + if ( !$this->unsuppress && $row->fa_deleted ) { $storeBatch[] = array( $deletedUrl, 'public', $destRel ); $this->cleanupBatch[] = $row->fa_storage_key; } } else { $archiveName = $row->fa_archive_name; - if( $archiveName == '' ) { + + if ( $archiveName == '' ) { // This was originally a current version; we // have to devise a new archive name for it. // Format is ! $timestamp = wfTimestamp( TS_UNIX, $row->fa_deleted_timestamp ); + do { $archiveName = wfTimestamp( TS_MW, $timestamp ) . '!' . $row->fa_name; $timestamp++; } while ( isset( $archiveNames[$archiveName] ) ); } + $archiveNames[$archiveName] = true; $destRel = $this->file->getArchiveRel( $archiveName ); $insertBatch[] = array( @@ -1654,19 +1773,23 @@ class LocalFileRestoreBatch { } $deleteIds[] = $row->fa_id; - if( !$this->unsuppress && $row->fa_deleted & File::DELETED_FILE ) { + + if ( !$this->unsuppress && $row->fa_deleted & File::DELETED_FILE ) { // private files can stay where they are $status->successCount++; } else { $storeBatch[] = array( $deletedUrl, 'public', $destRel ); $this->cleanupBatch[] = $row->fa_storage_key; } + $first = false; } + unset( $result ); // Add a warning to the status object for missing IDs $missingIds = array_diff( $this->ids, $idsPresent ); + foreach ( $missingIds as $id ) { $status->error( 'undelete-missing-filearchive', $id ); } @@ -1683,6 +1806,7 @@ class LocalFileRestoreBatch { // Store batch returned a critical error -- this usually means nothing was stored // Stop now and return an error $this->file->unlock(); + return $status; } @@ -1695,9 +1819,11 @@ class LocalFileRestoreBatch { if ( $insertCurrent ) { $dbw->insert( 'image', $insertCurrent, __METHOD__ ); } + if ( $insertBatch ) { $dbw->insert( 'oldimage', $insertBatch, __METHOD__ ); } + if ( $deleteIds ) { $dbw->delete( 'filearchive', array( 'fa_id IN (' . $dbw->makeList( $deleteIds ) . ')' ), @@ -1705,8 +1831,8 @@ class LocalFileRestoreBatch { } // If store batch is empty (all files are missing), deletion is to be considered successful - if( $status->successCount > 0 || !$storeBatch ) { - if( !$exists ) { + if ( $status->successCount > 0 || !$storeBatch ) { + if ( !$exists ) { wfDebug( __METHOD__ . " restored {$status->successCount} items, creating a new current\n" ); // Update site_stats @@ -1720,7 +1846,9 @@ class LocalFileRestoreBatch { $this->file->purgeHistory(); } } + $this->file->unlock(); + return $status; } @@ -1729,12 +1857,17 @@ class LocalFileRestoreBatch { */ function removeNonexistentFiles( $triplets ) { $files = $filteredTriplets = array(); - foreach( $triplets as $file ) + foreach ( $triplets as $file ) $files[$file[0]] = $file[0]; + $result = $this->file->repo->fileExistsBatch( $files, FSRepo::FILES_ONLY ); - foreach( $triplets as $file ) - if( $result[$file[0]] ) + + foreach ( $triplets as $file ) { + if ( $result[$file[0]] ) { $filteredTriplets[] = $file; + } + } + return $filteredTriplets; } @@ -1744,15 +1877,20 @@ class LocalFileRestoreBatch { function removeNonexistentFromCleanup( $batch ) { $files = $newBatch = array(); $repo = $this->file->repo; - foreach( $batch as $file ) { + + foreach ( $batch as $file ) { $files[$file] = $repo->getVirtualUrl( 'deleted' ) . '/' . rawurlencode( $repo->getDeletedHashPath( $file ) . $file ); } $result = $repo->fileExistsBatch( $files, FSRepo::FILES_ONLY ); - foreach( $batch as $file ) - if( $result[$file] ) + + foreach ( $batch as $file ) { + if ( $result[$file] ) { $newBatch[] = $file; + } + } + return $newBatch; } @@ -1764,13 +1902,16 @@ class LocalFileRestoreBatch { if ( !$this->cleanupBatch ) { return $this->file->repo->newGood(); } + $this->cleanupBatch = $this->removeNonexistentFromCleanup( $this->cleanupBatch ); + $status = $this->file->repo->cleanupDeletedBatch( $this->cleanupBatch ); + return $status; } } -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ /** * Helper class for file movement @@ -1811,23 +1952,30 @@ class LocalFileMoveBatch { array( 'oi_name' => $this->oldName ), __METHOD__ ); - while( $row = $this->db->fetchObject( $result ) ) { + + while ( $row = $this->db->fetchObject( $result ) ) { $oldName = $row->oi_archive_name; $bits = explode( '!', $oldName, 2 ); - if( count( $bits ) != 2 ) { + + if ( count( $bits ) != 2 ) { wfDebug( "Invalid old file name: $oldName \n" ); continue; } + list( $timestamp, $filename ) = $bits; - if( $this->oldName != $filename ) { + + if ( $this->oldName != $filename ) { wfDebug( "Invalid old file name: $oldName \n" ); continue; } + $this->oldCount++; + // Do we want to add those to oldCount? - if( $row->oi_deleted & File::DELETED_FILE ) { + if ( $row->oi_deleted & File::DELETED_FILE ) { continue; } + $this->olds[] = array( "{$archiveBase}/{$this->oldHash}{$oldName}", "{$archiveBase}/{$this->newHash}{$timestamp}!{$this->newName}" @@ -1848,13 +1996,15 @@ class LocalFileMoveBatch { wfDebugLog( 'imagemove', "Renamed {$this->file->name} in database: {$statusDb->successCount} successes, {$statusDb->failCount} failures" ); $statusMove = $repo->storeBatch( $triplets, FSRepo::DELETE_SOURCE ); wfDebugLog( 'imagemove', "Moved files for {$this->file->name}: {$statusMove->successCount} successes, {$statusMove->failCount} failures" ); - if( !$statusMove->isOk() ) { + + if ( !$statusMove->isOk() ) { wfDebugLog( 'imagemove', "Error in moving files: " . $statusMove->getWikiText() ); $this->db->rollback(); } $status->merge( $statusDb ); $status->merge( $statusMove ); + return $status; } @@ -1876,7 +2026,8 @@ class LocalFileMoveBatch { array( 'img_name' => $this->oldName ), __METHOD__ ); - if( $dbw->affectedRows() ) { + + if ( $dbw->affectedRows() ) { $status->successCount++; } else { $status->failCount++; @@ -1887,11 +2038,12 @@ class LocalFileMoveBatch { 'oldimage', array( 'oi_name' => $this->newName, - 'oi_archive_name = ' . $dbw->strreplace( 'oi_archive_name', $dbw->addQuotes($this->oldName), $dbw->addQuotes($this->newName) ), + 'oi_archive_name = ' . $dbw->strreplace( 'oi_archive_name', $dbw->addQuotes( $this->oldName ), $dbw->addQuotes( $this->newName ) ), ), array( 'oi_name' => $this->oldName ), __METHOD__ ); + $affected = $dbw->affectedRows(); $total = $this->oldCount; $status->successCount += $affected; @@ -1906,12 +2058,14 @@ class LocalFileMoveBatch { function getMoveTriplets() { $moves = array_merge( array( $this->cur ), $this->olds ); $triplets = array(); // The format is: (srcUrl, destZone, destUrl) - foreach( $moves as $move ) { + + foreach ( $moves as $move ) { // $move: (oldRelativePath, newRelativePath) $srcUrl = $this->file->repo->getVirtualUrl() . '/public/' . rawurlencode( $move[0] ); $triplets[] = array( $srcUrl, 'public', $move[1] ); wfDebugLog( 'imagemove', "Generated move triplet for {$this->file->name}: {$srcUrl} :: public :: {$move[1]}" ); } + return $triplets; } @@ -1920,16 +2074,22 @@ class LocalFileMoveBatch { */ function removeNonexistentFiles( $triplets ) { $files = array(); - foreach( $triplets as $file ) + + foreach ( $triplets as $file ) { $files[$file[0]] = $file[0]; + } + $result = $this->file->repo->fileExistsBatch( $files, FSRepo::FILES_ONLY ); $filteredTriplets = array(); - foreach( $triplets as $file ) - if( $result[$file[0]] ) { + + foreach ( $triplets as $file ) { + if ( $result[$file[0]] ) { $filteredTriplets[] = $file; } else { wfDebugLog( 'imagemove', "File {$file[0]} does not exist" ); } + } + return $filteredTriplets; } } diff --git a/includes/installer/CliInstaller.php b/includes/installer/CliInstaller.php index 24d0fd7ec4..69c9217ec1 100644 --- a/includes/installer/CliInstaller.php +++ b/includes/installer/CliInstaller.php @@ -101,14 +101,14 @@ class CliInstaller extends CoreInstaller { $this->showStatusMessage( $status ); echo "\n"; exit; - } elseif ( count($warnings) !== 0 ) { + } elseif ( count( $warnings ) !== 0 ) { foreach ( $status->getWikiTextArray( $warnings ) as $w ) { $this->showMessage( $w . wfMsg( 'ellipsis' ) . wfMsg( 'word-separator' ) ); } } - $this->showMessage( wfMsg( 'config-install-step-done' ) ."\n"); + $this->showMessage( wfMsg( 'config-install-step-done' ) . "\n" ); } public function showMessage( $msg /*, ... */ ) { @@ -119,5 +119,4 @@ class CliInstaller extends CoreInstaller { public function showStatusMessage( Status $status ) { $this->showMessage( $status->getWikiText() ); } - -} \ No newline at end of file +} diff --git a/maintenance/parserTests.inc b/maintenance/parserTests.inc index 05327c1065..d17ae72feb 100644 --- a/maintenance/parserTests.inc +++ b/maintenance/parserTests.inc @@ -76,6 +76,7 @@ class ParserTest { break; } } + $this->term = $this->color ? new AnsiTermColorer() : new DummyTermColorer(); @@ -163,6 +164,7 @@ class ParserTest { mt_srand( ++$this->fuzzSeed ); $totalLength = mt_rand( 1, $this->maxFuzzTestLength ); $input = ''; + while ( strlen( $input ) < $totalLength ) { $logHairLength = mt_rand( 0, 1000000 ) / 1000000 * $logMaxLength; $hairLength = min( intval( exp( $logHairLength ) ), $dictSize ); @@ -172,6 +174,7 @@ class ParserTest { $this->setupGlobals(); $parser = $this->getParser(); + // Run the test try { $parser->parse( $input, $title, $opts ); @@ -189,6 +192,7 @@ class ParserTest { } else { $numSuccess++; } + $numTotal++; $this->teardownGlobals(); $parser->__destruct(); @@ -199,6 +203,7 @@ class ParserTest { if ( $usage > 90 ) { echo "Out of memory:\n"; $memStats = $this->getMemoryBreakdown(); + foreach ( $memStats as $name => $usage ) { echo "$name: $usage\n"; } @@ -213,13 +218,16 @@ class ParserTest { */ function getFuzzInput( $filenames ) { $dict = ''; + foreach ( $filenames as $filename ) { $contents = file_get_contents( $filename ); preg_match_all( '/!!\s*input\n(.*?)\n!!\s*result/s', $contents, $matches ); + foreach ( $matches[1] as $match ) { $dict .= $match . "\n"; } } + return $dict; } @@ -228,25 +236,33 @@ class ParserTest { */ function getMemoryBreakdown() { $memStats = array(); + foreach ( $GLOBALS as $name => $value ) { $memStats['$' . $name] = strlen( serialize( $value ) ); } + $classes = get_declared_classes(); + foreach ( $classes as $class ) { $rc = new ReflectionClass( $class ); $props = $rc->getStaticProperties(); $memStats[$class] = strlen( serialize( $props ) ); $methods = $rc->getMethods(); + foreach ( $methods as $method ) { $memStats[$class] += strlen( serialize( $method->getStaticVariables() ) ); } } + $functions = get_defined_functions(); + foreach ( $functions['user'] as $function ) { $rf = new ReflectionFunction( $function ); $memStats["$function()"] = strlen( serialize( $rf->getStaticVariables() ) ); } + asort( $memStats ); + return $memStats; } @@ -270,27 +286,33 @@ class ParserTest { $this->recorder->start(); $this->setupDatabase(); $ok = true; + foreach ( $filenames as $filename ) { $tests = new TestFileIterator( $filename, $this ); $ok = $this->runTests( $tests ) && $ok; } + $this->teardownDatabase(); $this->recorder->report(); $this->recorder->end(); + return $ok; } function runTests( $tests ) { $ok = true; + foreach ( $tests as $i => $t ) { $result = $this->runTest( $t['test'], $t['input'], $t['result'], $t['options'], $t['config'] ); $ok = $ok && $result; $this->recorder->record( $t['test'], $result ); } + if ( $this->showProgress ) { print "\n"; } + return $ok; } @@ -299,16 +321,21 @@ class ParserTest { */ function getParser( $preprocessor = null ) { global $wgParserConf; + $class = $wgParserConf['class']; $parser = new $class( array( 'preprocessorClass' => $preprocessor ) + $wgParserConf ); + foreach ( $this->hooks as $tag => $callback ) { $parser->setHook( $tag, $callback ); } + foreach ( $this->functionHooks as $tag => $bits ) { list( $callback, $flags ) = $bits; $parser->setFunctionHook( $tag, $callback, $flags ); } + wfRunHooks( 'ParserTestParser', array( &$parser ) ); + return $parser; } @@ -369,15 +396,21 @@ class ParserTest { $out = $output->getText(); if ( isset( $opts['showtitle'] ) ) { - if ( $output->getTitleText() ) $title = $output->getTitleText(); + if ( $output->getTitleText() ) { + $title = $output->getTitleText(); + } + $out = "$title\n$out"; } + if ( isset( $opts['ill'] ) ) { $out = $this->tidy( implode( ' ', $output->getLanguageLinks() ) ); } elseif ( isset( $opts['cat'] ) ) { global $wgOut; + $wgOut->addCategoryLinks( $output->getCategories() ); $cats = $wgOut->getCategoryLinks(); + if ( isset( $cats['normal'] ) ) { $out = $this->tidy( implode( ' ', $cats['normal'] ) ); } else { @@ -398,7 +431,6 @@ class ParserTest { } } - /** * Use a regex to find out the value of an option * @param $key String: name of option val to retrieve @@ -407,6 +439,7 @@ class ParserTest { */ private static function getOptionValue( $key, $opts, $default ) { $key = strtolower( $key ); + if ( isset( $opts[$key] ) ) { return $opts[$key]; } else { @@ -474,6 +507,7 @@ class ParserTest { if ( substr( $opt, 0, 1 ) == '"' ) { return substr( $opt, 1, -1 ); } + if ( substr( $opt, 0, 2 ) == '[[' ) { return substr( $opt, 2, -2 ); } @@ -565,12 +599,15 @@ class ParserTest { } $this->savedGlobals = array(); + foreach ( $settings as $var => $val ) { if ( array_key_exists( $var, $GLOBALS ) ) { $this->savedGlobals[$var] = $GLOBALS[$var]; } + $GLOBALS[$var] = $val; } + $langObj = Language::factory( $lang ); $GLOBALS['wgLang'] = $langObj; $GLOBALS['wgContLang'] = $langObj; @@ -578,6 +615,7 @@ class ParserTest { $GLOBALS['wgOut'] = new OutputPage; global $wgHooks; + $wgHooks['ParserTestParser'][] = 'ParserTestParserHook::setup'; $wgHooks['ParserTestParser'][] = 'ParserTestStaticParserHook::setup'; $wgHooks['ParserGetVariableValueTs'][] = 'ParserTest::getFakeTimestamp'; @@ -594,6 +632,7 @@ class ParserTest { */ private function listTables() { global $wgDBtype; + $tables = array( 'user', 'user_properties', 'page', 'page_restrictions', 'protected_titles', 'revision', 'text', 'pagelinks', 'imagelinks', 'categorylinks', 'templatelinks', 'externallinks', 'langlinks', 'iwlinks', @@ -621,12 +660,15 @@ class ParserTest { */ public function setupDatabase() { global $wgDBprefix, $wgDBtype; + if ( $this->databaseSetupDone ) { return; } + if ( $wgDBprefix === 'parsertest_' || ( $wgDBtype == 'oracle' && $wgDBprefix === 'pt_' ) ) { throw new MWException( 'setupDatabase should be called before setupGlobals' ); } + $this->databaseSetupDone = true; $this->oldTablePrefix = $wgDBprefix; @@ -663,9 +705,11 @@ class ParserTest { } elseif ( $db->tableExists( $tbl ) ) { $db->query( "DROP TABLE $newTableName" ); } + # Create new table $db->duplicateTableStructure( $oldTableName, $newTableName, $temporary ); } + if ( $wgDBtype == 'oracle' ) $db->query( 'BEGIN FILL_WIKI_INFO; END;' ); @@ -737,7 +781,7 @@ class ParserTest { 'media_type' => MEDIATYPE_BITMAP, 'mime' => 'image/jpeg', 'metadata' => serialize( array() ), - 'sha1' => sha1(''), + 'sha1' => sha1( '' ), 'fileExists' => true ), $db->timestamp( '20010115123500' ), $user ); @@ -751,7 +795,7 @@ class ParserTest { 'media_type' => MEDIATYPE_BITMAP, 'mime' => 'image/jpeg', 'metadata' => serialize( array() ), - 'sha1' => sha1(''), + 'sha1' => sha1( '' ), 'fileExists' => true ), $db->timestamp( '20010115123500' ), $user ); } @@ -775,6 +819,7 @@ class ParserTest { public function teardownDatabase() { global $wgDBtype; + if ( !$this->databaseSetupDone ) { return; } @@ -782,6 +827,7 @@ class ParserTest { $this->changePrefix( $this->oldTablePrefix ); $this->databaseSetupDone = false; + if ( $this->useTemporaryTables ) { # Don't need to do anything return; @@ -789,12 +835,14 @@ class ParserTest { $tables = $this->listTables(); $db = wfGetDB( DB_MASTER ); + foreach ( $tables as $table ) { $sql = $wgDBtype == 'oracle' ? "DROP TABLE pt_$table DROP CONSTRAINTS" : "DROP TABLE `parsertest_$table`"; $db->query( $sql ); } - if ($wgDBtype == 'oracle') - $db->query('BEGIN FILL_WIKI_INFO; END;'); + + if ( $wgDBtype == 'oracle' ) + $db->query( 'BEGIN FILL_WIKI_INFO; END;' ); } /** @@ -805,8 +853,10 @@ class ParserTest { */ private function setupUploadDir() { global $IP; + if ( $this->keepUploads ) { $dir = wfTempDir() . '/mwParser-images'; + if ( is_dir( $dir ) ) { return $dir; } @@ -814,15 +864,17 @@ class ParserTest { $dir = wfTempDir() . "/mwParser-" . mt_rand() . "-images"; } - //wfDebug( "Creating upload directory $dir\n" ); + // wfDebug( "Creating upload directory $dir\n" ); if ( file_exists( $dir ) ) { wfDebug( "Already exists!\n" ); return $dir; } + wfMkdirParents( $dir . '/3/3a' ); copy( "$IP/skins/monobook/headbg.jpg", "$dir/3/3a/Foobar.jpg" ); wfMkdirParents( $dir . '/0/09' ); copy( "$IP/skins/monobook/headbg.jpg", "$dir/0/09/Bad.jpg" ); + return $dir; } @@ -833,6 +885,7 @@ class ParserTest { private function teardownGlobals() { RepoGroup::destroySingleton(); LinkCache::singleton()->clear(); + foreach ( $this->savedGlobals as $var => $val ) { $GLOBALS[$var] = $val; } @@ -924,6 +977,7 @@ class ParserTest { if ( $this->showProgress ) { print $this->term->color( '1;32' ) . 'PASSED' . $this->term->reset() . "\n"; } + return true; } @@ -943,10 +997,13 @@ class ParserTest { # test, in case it succeeded. Show it now: $this->showTesting( $desc ); } + print $this->term->color( '31' ) . 'FAILED!' . $this->term->reset() . "\n"; + if ( $this->showOutput ) { print "--- Expected ---\n$result\n--- Actual ---\n$html\n"; } + if ( $this->showDiffs ) { print $this->quickDiff( $result, $html ); if ( !$this->wellFormed( $html ) ) { @@ -954,6 +1011,7 @@ class ParserTest { } } } + return false; } @@ -1034,11 +1092,13 @@ class ParserTest { $wgCapitalLinks = true; // We only need this from SetupGlobals() See r70917#c8637 $title = Title::newFromText( $name ); + if ( is_null( $title ) ) { wfDie( "invalid title at line $line\n" ); } $aid = $title->getArticleID( GAID_FOR_UPDATE ); + if ( $aid != 0 ) { wfDie( "duplicate article '$name' at line $line\n" ); } @@ -1059,13 +1119,16 @@ class ParserTest { */ public function requireHook( $name ) { global $wgParser; + $wgParser->firstCallInit( ); // make sure hooks are loaded. + if ( isset( $wgParser->mTagHooks[$name] ) ) { $this->hooks[$name] = $wgParser->mTagHooks[$name]; } else { echo " This test suite requires the '$name' hook extension, skipping.\n"; return false; } + return true; } @@ -1079,13 +1142,16 @@ class ParserTest { */ public function requireFunctionHook( $name ) { global $wgParser; + $wgParser->firstCallInit( ); // make sure hooks are loaded. + if ( isset( $wgParser->mFunctionHooks[$name] ) ) { $this->functionHooks[$name] = $wgParser->mFunctionHooks[$name]; } else { echo " This test suite requires the '$name' function hook extension, skipping.\n"; return false; } + return true; } @@ -1099,9 +1165,11 @@ class ParserTest { */ private function tidy( $text ) { global $wgUseTidy; + if ( $wgUseTidy ) { $text = MWTidy::tidy( $text ); } + return $text; } @@ -1123,9 +1191,12 @@ class ParserTest { $fragment = $this->extractFragment( $html, $position ); $this->mXmlError = "$err at byte $position:\n$fragment"; xml_parser_free( $parser ); + return false; } + xml_parser_free( $parser ); + return true; } @@ -1150,6 +1221,7 @@ class ParserTest { $this->term->color( 31 ) . '^' . $this->term->color( 0 ); + return "$display\n$caret"; } @@ -1171,7 +1243,9 @@ class AnsiTermColorer { */ public function color( $color ) { global $wgCommandLineDarkBg; + $light = $wgCommandLineDarkBg ? "1;" : "0;"; + return "\x1b[{$light}{$color}m"; } @@ -1230,13 +1304,16 @@ class TestRecorder { function reportPercentage( $success, $total ) { $ratio = wfPercent( 100 * $success / $total ); print $this->term->color( 1 ) . "Passed $success of $total tests ($ratio)... "; + if ( $success == $total ) { print $this->term->color( 32 ) . "ALL TESTS PASSED!"; } else { $failed = $total - $success ; print $this->term->color( 31 ) . "$failed tests failed!"; } + print $this->term->reset() . "\n"; + return ( $success == $total ); } } @@ -1253,6 +1330,7 @@ class DbTestPreviewer extends TestRecorder { */ function __construct( $parent ) { parent::__construct( $parent ); + $this->lb = wfGetLBFactory()->newMainLB(); // This connection will have the wiki's table prefix, not parsertest_ $this->db = $this->lb->getConnection( DB_MASTER ); @@ -1274,6 +1352,7 @@ class DbTestPreviewer extends TestRecorder { // We'll make comparisons against the previous run later... $this->prevRun = $this->db->selectField( 'testrun', 'MAX(tr_id)' ); } + $this->results = array(); } @@ -1301,6 +1380,7 @@ class DbTestPreviewer extends TestRecorder { $res = $this->db->select( 'testitem', array( 'ti_name', 'ti_success' ), array( 'ti_run' => $this->prevRun ), __METHOD__ ); + foreach ( $res as $row ) { if ( !$this->parent->regex || preg_match( "/{$this->parent->regex}/i", $row->ti_name ) ) @@ -1321,6 +1401,7 @@ class DbTestPreviewer extends TestRecorder { } else /* if ( $prevResults[$test] == 0 )*/ { $before = 'f'; } + if ( !isset( $this->results[$test] ) ) { $after = 'n'; } elseif ( $this->results[$test] == 1 ) { @@ -1328,7 +1409,9 @@ class DbTestPreviewer extends TestRecorder { } else /*if ( $this->results[$test] == 0 ) */ { $after = 'f'; } + $code = $before . $after; + if ( isset( $table[$code] ) ) { $breakdown[$code][$test] = $this->getTestStatusInfo( $test, $after ); } @@ -1339,6 +1422,7 @@ class DbTestPreviewer extends TestRecorder { if ( !empty( $breakdown[$code] ) ) { $count = count( $breakdown[$code] ); printf( "\n%4d %s\n", $count, $label ); + foreach ( $breakdown[$code] as $differing_test_name => $statusInfo ) { print " * $differing_test_name [$statusInfo]\n"; } @@ -1347,6 +1431,7 @@ class DbTestPreviewer extends TestRecorder { } else { print "No previous test runs to compare against.\n"; } + print "\n"; parent::report(); } @@ -1357,7 +1442,6 @@ class DbTestPreviewer extends TestRecorder { * which have never passed (which are more change requests than regressions). */ private function getTestStatusInfo( $testname, $after ) { - // If we're looking at a test that has just been removed, then say when it first appeared. if ( $after == 'n' ) { $changedRun = $this->db->selectField ( 'testitem', @@ -1368,6 +1452,7 @@ class DbTestPreviewer extends TestRecorder { array( 'tr_date', 'tr_mw_version' ), array( 'tr_id' => $changedRun ), __METHOD__ ); + return "First recorded appearance: " . date( "d-M-Y H:i:s", strtotime ( $appear->tr_date ) ) . ", " . $appear->tr_mw_version; @@ -1378,6 +1463,7 @@ class DbTestPreviewer extends TestRecorder { $conds = array( 'ti_name' => $testname, 'ti_success' => ( $after == 'f' ? "1" : "0" ) ); + if ( $this->curRun ) { $conds[] = "ti_run != " . $this->db->addQuotes ( $this->curRun ); } @@ -1412,6 +1498,7 @@ class DbTestPreviewer extends TestRecorder { } else { $postDate = 'now'; } + return ( $after == "f" ? "Introduced" : "Fixed" ) . " between " . date( "d-M-Y H:i:s", strtotime ( $pre->tr_date ) ) . ", " . $pre->tr_mw_version . " and $postDate"; @@ -1444,12 +1531,14 @@ class DbTestRecorder extends DbTestPreviewer { or ! $this->db->tableExists( 'testitem' ) ) { print "WARNING> `testrun` table not found in database. Trying to create table.\n"; - if ( $wgDBtype === 'postgres' ) + if ( $wgDBtype === 'postgres' ) { $this->db->sourceFile( dirname( __FILE__ ) . '/testRunner.postgres.sql' ); - elseif ( $wgDBtype === 'oracle' ) + } elseif ( $wgDBtype === 'oracle' ) { $this->db->sourceFile( dirname( __FILE__ ) . '/testRunner.ora.sql' ); - else + } else { $this->db->sourceFile( dirname( __FILE__ ) . '/testRunner.sql' ); + } + echo "OK, resuming.\n"; } @@ -1464,10 +1553,11 @@ class DbTestRecorder extends DbTestPreviewer { 'tr_uname' => php_uname() ), __METHOD__ ); - if ( $wgDBtype === 'postgres' ) + if ( $wgDBtype === 'postgres' ) { $this->curRun = $this->db->currentSequenceValue( 'testrun_id_seq' ); - else + } else { $this->curRun = $this->db->insertId(); + } } /** @@ -1478,6 +1568,7 @@ class DbTestRecorder extends DbTestPreviewer { */ function record( $test, $result ) { parent::record( $test, $result ); + $this->db->insert( 'testitem', array( 'ti_run' => $this->curRun, @@ -1491,6 +1582,7 @@ class DbTestRecorder extends DbTestPreviewer { class RemoteTestRecorder extends TestRecorder { function start() { parent::start(); + $this->results = array(); $this->ping( 'running' ); } @@ -1532,6 +1624,7 @@ class RemoteTestRecorder extends TestRecorder { $revId, $status, ); + if ( $status == "complete" ) { $message[] = $jsonResults; } @@ -1546,21 +1639,26 @@ class RemoteTestRecorder extends TestRecorder { 'status' => $status, 'hmac' => $hmac, ); + if ( $status == "complete" ) { $postData['results'] = $jsonResults; } + $response = $this->post( $remote['api-url'], $postData ); if ( $response === false ) { print "CodeReview info upload failed to reach server.\n"; exit( 1 ); } + $responseData = FormatJson::decode( $response, true ); + if ( !is_array( $responseData ) ) { print "CodeReview API response not recognized...\n"; wfDebug( "Unrecognized CodeReview API response: $response\n" ); exit( 1 ); } + if ( isset( $responseData['error'] ) ) { $code = $responseData['error']['code']; $info = $responseData['error']['info']; @@ -1588,13 +1686,17 @@ class TestFileIterator implements Iterator { $this->file = $file; $this->fh = fopen( $this->file, "rt" ); + if ( !$this->fh ) { wfDie( "Couldn't open file '$file'\n" ); } $this->parser = $parser; - if ( $this->parser ) $this->parser->showRunFile( wfRelativePath( $this->file, $IP ) ); + if ( $this->parser ) { + $this->parser->showRunFile( wfRelativePath( $this->file, $IP ) ); + } + $this->lineNum = $this->index = 0; } @@ -1606,6 +1708,7 @@ class TestFileIterator implements Iterator { if ( fseek( $this->fh, 0 ) ) { wfDie( "Couldn't fseek to the start of '$this->file'\n" ); } + $this->index = -1; $this->lineNum = 0; $this->eof = false; @@ -1642,68 +1745,89 @@ class TestFileIterator implements Iterator { while ( false !== ( $line = fgets( $this->fh ) ) ) { $this->lineNum++; $matches = array(); + if ( preg_match( '/^!!\s*(\w+)/', $line, $matches ) ) { $section = strtolower( $matches[1] ); + if ( $section == 'endarticle' ) { if ( !isset( $data['text'] ) ) { wfDie( "'endarticle' without 'text' at line {$this->lineNum} of $this->file\n" ); } + if ( !isset( $data['article'] ) ) { wfDie( "'endarticle' without 'article' at line {$this->lineNum} of $this->file\n" ); } + if ( $this->parser ) { $this->parser->addArticle( $this->parser->chomp( $data['article'] ), $this->parser->chomp( $data['text'] ), $this->lineNum ); } + $data = array(); $section = null; + continue; } + if ( $section == 'endhooks' ) { if ( !isset( $data['hooks'] ) ) { wfDie( "'endhooks' without 'hooks' at line {$this->lineNum} of $this->file\n" ); } + foreach ( explode( "\n", $data['hooks'] ) as $line ) { $line = trim( $line ); + if ( $line ) { if ( $this->parser && !$this->parser->requireHook( $line ) ) { return false; } } } + $data = array(); $section = null; + continue; } + if ( $section == 'endfunctionhooks' ) { if ( !isset( $data['functionhooks'] ) ) { wfDie( "'endfunctionhooks' without 'functionhooks' at line {$this->lineNum} of $this->file\n" ); } + foreach ( explode( "\n", $data['functionhooks'] ) as $line ) { $line = trim( $line ); + if ( $line ) { if ( $this->parser && !$this->parser->requireFunctionHook( $line ) ) { return false; } } } + $data = array(); $section = null; + continue; } + if ( $section == 'end' ) { if ( !isset( $data['test'] ) ) { wfDie( "'end' without 'test' at line {$this->lineNum} of $this->file\n" ); } + if ( !isset( $data['input'] ) ) { wfDie( "'end' without 'input' at line {$this->lineNum} of $this->file\n" ); } + if ( !isset( $data['result'] ) ) { wfDie( "'end' without 'result' at line {$this->lineNum} of $this->file\n" ); } + if ( !isset( $data['options'] ) ) { $data['options'] = ''; } + if ( !isset( $data['config'] ) ) $data['config'] = ''; @@ -1713,14 +1837,18 @@ class TestFileIterator implements Iterator { # disabled test $data = array(); $section = null; + continue; } + global $wgUseTeX; + if ( $this->parser && preg_match( '/\\bmath\\b/i', $data['options'] ) && !$wgUseTeX ) { # don't run math tests if $wgUseTeX is set to false in LocalSettings $data = array(); $section = null; + continue; } @@ -1734,19 +1862,24 @@ class TestFileIterator implements Iterator { } else { $this->test['test'] = $data['test']; } + return true; } + if ( isset ( $data[$section] ) ) { wfDie( "duplicate section '$section' at line {$this->lineNum} of $this->file\n" ); } + $data[$section] = ''; + continue; } + if ( $section ) { $data[$section] .= $line; } } + return false; } } - diff --git a/maintenance/tests/UploadFromUrlTestSuite.php b/maintenance/tests/UploadFromUrlTestSuite.php index 8393eb7f6f..927154b5be 100644 --- a/maintenance/tests/UploadFromUrlTestSuite.php +++ b/maintenance/tests/UploadFromUrlTestSuite.php @@ -1,13 +1,13 @@ 'LocalRepo', 'name' => 'local', - 'directory' => wfTempDir().'/test-repo', + 'directory' => wfTempDir() . '/test-repo', 'url' => 'http://example.com/images', - 'deletedDir' => wfTempDir().'/test-repo/delete', + 'deletedDir' => wfTempDir() . '/test-repo/delete', 'hashLevels' => 2, 'transformVia404' => false, ); @@ -45,7 +45,7 @@ class UploadFromUrlTestSuite extends PHPUnit_Framework_TestSuite $messageMemc =& wfGetMessageCacheStorage(); $parserMemc =& wfGetParserCacheStorage(); - //$wgContLang = new StubContLang; + // $wgContLang = new StubContLang; $wgUser = new User; $wgLang = new StubUserLang; $wgOut = new StubObject( 'wgOut', 'OutputPage' ); @@ -55,7 +55,9 @@ class UploadFromUrlTestSuite extends PHPUnit_Framework_TestSuite $wgMessageCache = new StubObject( 'wgMessageCache', 'MessageCache', array( $messageMemc, $wgUseDatabaseMessages, $wgMsgCacheExpiry ) ); - if ( $wgStyleDirectory === false ) $wgStyleDirectory = "$IP/skins"; + if ( $wgStyleDirectory === false ) { + $wgStyleDirectory = "$IP/skins"; + } } @@ -106,7 +108,6 @@ class UploadFromUrlTestSuite extends PHPUnit_Framework_TestSuite ); } - /** * Delete the specified files, if they exist. * @@ -141,8 +142,10 @@ class UploadFromUrlTestSuite extends PHPUnit_Framework_TestSuite */ private function setupUploadDir() { global $IP; + if ( $this->keepUploads ) { $dir = wfTempDir() . '/mwParser-images'; + if ( is_dir( $dir ) ) { return $dir; } @@ -151,22 +154,22 @@ class UploadFromUrlTestSuite extends PHPUnit_Framework_TestSuite } wfDebug( "Creating upload directory $dir\n" ); + if ( file_exists( $dir ) ) { wfDebug( "Already exists!\n" ); return $dir; } + wfMkdirParents( $dir . '/3/3a' ); copy( "$IP/skins/monobook/headbg.jpg", "$dir/3/3a/Foobar.jpg" ); wfMkdirParents( $dir . '/0/09' ); copy( "$IP/skins/monobook/headbg.jpg", "$dir/0/09/Bad.jpg" ); + return $dir; } - public static function suite() - { + public static function suite() { return new UploadFromUrlTestSuite( 'UploadFromUrlTest' ); } - } - diff --git a/maintenance/tests/selenium/Selenium.php b/maintenance/tests/selenium/Selenium.php index 48e505dcd5..7594f1f2c1 100644 --- a/maintenance/tests/selenium/Selenium.php +++ b/maintenance/tests/selenium/Selenium.php @@ -5,6 +5,7 @@ */ require( 'Testing/Selenium.php' ); + class Selenium { protected static $_instance = null; public $isStarted = false; @@ -36,7 +37,7 @@ class Selenium { if ( null === self::$_instance ) { self::$_instance = $this; } else { - throw new MWException("Already have one Selenium instance."); + throw new MWException( "Already have one Selenium instance." ); } } @@ -75,9 +76,11 @@ class Selenium { $this->type( 'wpName1', $this->user ); $this->type( 'wpPassword1', $this->pass ); $this->click( "//input[@id='wpLoginAttempt']" ); - $this->waitForPageToLoad(5000); - //after login we redirect to the main page. So check whether the "Prefernces" top menu item exists + $this->waitForPageToLoad( 5000 ); + + // after login we redirect to the main page. So check whether the "Prefernces" top menu item exists $value = $this->isElementPresent( "//li[@id='pt-preferences']" ); + if ( $value != true ) { throw new Testing_Selenium_Exception( "Login Failed" ); } @@ -95,7 +98,7 @@ class Selenium { public function loadPage( $title, $action ) { $this->open( self::$url . '/index.php?title=' . $title . '&action=' . $action ); } - + public function setLogger( $logger ) { $this->logger = $logger; } @@ -138,14 +141,17 @@ class Selenium { public function setBrowser( $b ) { $browsers = $this->setupBrowsers(); + + if ( !isset( $browsers[$b] ) ) { throw new MWException( "Invalid Browser: $b.\n" ); } + $this->browser = $browsers[$b]; } public function __call( $name, $args ) { - $t = call_user_func_array( array( $this->tester, $name), $args ); + $t = call_user_func_array( array( $this->tester, $name ), $args ); return $t; } @@ -179,14 +185,16 @@ class Selenium { public function setBrowser( $b ) { $browsers = $this->setupBrowsers(); + if ( !isset( $browsers[$b] ) ) { throw new MWException( "Invalid Browser: $b.\n" ); } + $this->browser = $browsers[$b]; } public function __call( $name, $args ) { - $t = call_user_func_array( array( $this->tester, $name), $args ); + $t = call_user_func_array( array( $this->tester, $name ), $args ); return $t; } diff --git a/maintenance/tests/selenium/SeleniumTestSuite.php b/maintenance/tests/selenium/SeleniumTestSuite.php index a86f5a9f19..0b31c833af 100644 --- a/maintenance/tests/selenium/SeleniumTestSuite.php +++ b/maintenance/tests/selenium/SeleniumTestSuite.php @@ -11,7 +11,7 @@ class SeleniumTestSuite extends PHPUnit_Framework_TestSuite { public function setUp() { // Hack because because PHPUnit version 3.0.6 which is on prototype does not - //run setUp as part of TestSuite::run + // run setUp as part of TestSuite::run if ( $this->isSetUp ) { return; } @@ -34,4 +34,3 @@ class SeleniumTestSuite extends PHPUnit_Framework_TestSuite { $this->selenium->loadPage( $title, $action ); } } -