/** * @covers MWDebug::deprecated */ public function testAvoidNonConsecutivesDuplicateDeprecations() { MWDebug::deprecated('wfOldFunction', '1.0', 'component'); MWDebug::warning('some warning'); MWDebug::log('we could have logged something too'); // Another deprecation MWDebug::deprecated('wfOldFunction', '1.0', 'component'); // assertCount() not available on WMF integration server $this->assertEquals(3, count(MWDebug::getLog()), "Only one deprecated warning per function should be kept"); }
/** * Acquire a Redis connection. * * @access protected * @param string [Optiona] Server group key. * Example: 'cache' would look up $wgRedisServers['cached'] * Default: Uses the first index of $wgRedisServers. * @param array [Optional] Additional options, will merge and overwrite default options. * - connectTimeout : The timeout for new connections, in seconds. * Optional, default is 1 second. * - persistent : Set this to true to allow connections to persist across * multiple web requests. False by default. * - password : The authentication password, will be sent to Redis in clear text. * Optional, if it is unspecified, no AUTH command will be sent. * - serializer : Set to "php", "igbinary", or "none". Default is "php". * @param boolean [Optional] Force a new connection, useful when forking processes. * @return mixed Object RedisConnRef or false on failure. */ public static function getClient($group = null, $options = [], $newConnection = false) { global $wgRedisServers; if (!extension_loaded('redis')) { throw new MWException(__METHOD__ . " - The PHP Redis extension is not available. Please enable it on the server to use RedisCache."); } if (empty($wgRedisServers) || !is_array($wgRedisServers)) { MWDebug::log(__METHOD__ . " - \$wgRedisServers must be configured for RedisCache to function."); return false; } if (empty($group)) { $group = 0; $server = current($wgRedisServers); } else { $server = $wgRedisServers[$group]; } if ($newConnection === false && array_key_exists($group, self::$servers)) { return self::$servers[$group]; } if (empty($server) || !is_array($server)) { throw new MWException(__METHOD__ . " - An invalid server group key was passed."); } $pool = \RedisConnectionPool::singleton(array_merge($server['options'], $options)); $redis = $pool->getConnection($server['host'] . ":" . $server['port']); //Concatenate these together for MediaWiki weirdness so it can split them later. if ($redis instanceof RedisConnRef) { //Set up any extra options. RedisConnectionPool does not handle the prefix automatically. if (!empty($server['options']['prefix'])) { $redis->setOption(Redis::OPT_PREFIX, $server['options']['prefix']); } try { $pong = $redis->ping(); if ($pong === '+PONG') { self::$servers[$group] = $redis; } else { $redis = false; } } catch (RedisException $e) { //People using HAProxy will find it will lie about a Redis cluster being healthy when the master is down, but the slaves are up. Doing a PING will cause an immediate disconnect. self::$lastError = $e->getMessage(); $redis = false; } } return $redis; }
/** * @covers MWDebug::appendDebugInfoToApiResult */ public function testAppendDebugInfoToApiResultXmlFormat() { $request = $this->newApiRequest(['action' => 'help', 'format' => 'xml'], '/api.php?action=help&format=xml'); $context = new RequestContext(); $context->setRequest($request); $apiMain = new ApiMain($context); $result = new ApiResult($apiMain); MWDebug::appendDebugInfoToApiResult($context, $result); $this->assertInstanceOf('ApiResult', $result); $data = $result->getResultData(); $expectedKeys = ['mwVersion', 'phpEngine', 'phpVersion', 'gitRevision', 'gitBranch', 'gitViewUrl', 'time', 'log', 'debugLog', 'queries', 'request', 'memory', 'memoryPeak', 'includes', '_element']; foreach ($expectedKeys as $expectedKey) { $this->assertArrayHasKey($expectedKey, $data['debuginfo'], "debuginfo has {$expectedKey}"); } $xml = ApiFormatXml::recXmlPrint('help', $data); // exception not thrown $this->assertInternalType('string', $xml); }
/** * Main Hook */ public static function hOutputPageParserOutput(&$op, $parserOutput) { $action = $op->parserOptions()->getUser()->getRequest()->getVal("action"); if ($action == 'edit' || $action == 'submit' || $action == 'history') { return true; } global $wgTitle, $wgOut; $ns = $wgTitle->getNsText(); $name = $wgTitle->getPrefixedDBKey(); $text = $parserOutput->getText(); $categories = $wgTitle->getParentCategories(); $categories = array_keys($categories); var_dump($categories); $categories = array_map(function ($cat) { return Title::newFromText($cat, NS_CATEGORY)->getText(); }, $categories); var_dump($categories); //MWDebug::warning( "ns=" . $ns ) ; //MWDebug::warning( "name=" . $name ) ; $nsheader = "hf-nsheader-{$ns}"; $nsfooter = "hf-nsfooter-{$ns}"; $header = "hf-header-{$name}"; $footer = "hf-footer-{$name}"; /** * headers/footers are wrapped around page content. * header: page + namespace + categories in reverse alphabetical order * footer: categories in alphabetical order + namespace + page */ $text = '<div class="hf-header">' . self::conditionalInclude($text, '__NOHEADER__', $header) . '</div>' . $text; $text = '<div class="hf-nsheader">' . self::conditionalInclude($text, '__NONSHEADER__', $nsheader) . '</div>' . $text; foreach ($categories as &$category) { MWDebug::warning("cat=" . $category); $catheader = "hf-catheader-{$category}"; $text = '<div class="hf-catheader">' . self::conditionalInclude($text, '__NOCATHEADER__', $catheader) . '</div>' . $text; $catfooter = "hf-catfooter-{$category}"; $text .= '<div class="hf-catfooter">' . self::conditionalInclude($text, '__NOCATFOOTER__', $catfooter) . '</div>'; } //// $text .= '<div class="hf-footer">' . self::conditionalInclude($text, '__NOFOOTER__', $footer) . '</div>'; $text .= '<div class="hf-nsfooter">' . self::conditionalInclude($text, '__NONSFOOTER__', $nsfooter) . '</div>'; $parserOutput->setText($text); return true; }
public function execute($parameters) { $out = $this->getOutput(); $skin = $this->getSkin(); $this->setHeaders(); $out->setArticleBodyOnly(true); // Default modules copied from OutputPage::addDefaultModules $out->addModules(array('mediawiki.user', 'mediawiki.page.startup', 'mediawiki.page.ready')); $out->addModules(array('ext.cx.header', 'ext.cx.stats')); // Load legacy modules if any, for the skin. // Some wikis have Common.js scripts that depend on this module. $defaultSkinModules = $skin->getDefaultModules(); $out->addModules($defaultSkinModules['legacy']); Hooks::run('BeforePageDisplay', array(&$out, &$skin)); $out->addHTML($out->headElement($skin)); $out->addHTML(Html::element('noscript', array(), $this->msg('cx-javascript')->text())); $out->addHtml(MWDebug::getDebugHTML($this->getContext())); $toolbarList = Html::rawElement('ul', null, $skin->getPersonalToolsList()); $out->addHTML(Html::rawElement('div', array('id' => 'p-personal'), $toolbarList)); $out->addHTML($skin->bottomScripts()); $out->addHTML('</body></html>'); }
/** * Send a warning either to the debug log or in a PHP error depending on * $wgDevelopmentWarnings * * @param $msg String: message to send * @param $callerOffset Integer: number of items to go back in the backtrace to * find the correct caller (1 = function calling wfWarn, ...) * @param $level Integer: PHP error level; only used when $wgDevelopmentWarnings * is true */ function wfWarn($msg, $callerOffset = 1, $level = E_USER_NOTICE) { global $wgDevelopmentWarnings; MWDebug::warning($msg, $callerOffset + 2); $callers = wfDebugBacktrace(); if (isset($callers[$callerOffset + 1])) { $callerfunc = $callers[$callerOffset + 1]; $callerfile = $callers[$callerOffset]; if (isset($callerfile['file']) && isset($callerfile['line'])) { $file = $callerfile['file'] . ' at line ' . $callerfile['line']; } else { $file = '(internal function)'; } $func = ''; if (isset($callerfunc['class'])) { $func .= $callerfunc['class'] . '::'; } if (isset($callerfunc['function'])) { $func .= $callerfunc['function']; } $msg .= " [Called from {$func} in {$file}]"; } if ($wgDevelopmentWarnings) { trigger_error($msg, $level); } else { wfDebug("{$msg}\n"); } }
/** * Generate debug data HTML for displaying at the bottom of the main content * area. * @return String HTML containing debug data, if enabled (otherwise empty). */ protected function generateDebugHTML() { global $wgShowDebug; $html = MWDebug::getDebugHTML($this->getContext()); if ($wgShowDebug) { $listInternals = $this->formatDebugHTML($this->getOutput()->mDebugtext); $html .= "\n<hr />\n<strong>Debug data:</strong><ul id=\"mw-debug-html\">" . $listInternals . "</ul>\n"; } return $html; }
/** * Output the basic end-page trail including bottomscripts, reporttime, and * debug stuff. This should be called right before outputting the closing * body and html tags. */ function printTrail() { echo MWDebug::getDebugHTML($this->getSkin()->getContext()); $this->html('bottomscripts'); /* JS call to runBodyOnloadHook */ $this->html('reporttime'); }
/** * Finally, all the text has been munged and accumulated into * the object, let's actually output it: */ public function output() { global $wgLanguageCode, $wgDebugRedirects, $wgMimeType, $wgVaryOnXFP, $wgUseAjax, $wgResponsiveImages; if ($this->mDoNothing) { return; } wfProfileIn(__METHOD__); $response = $this->getRequest()->response(); if ($this->mRedirect != '') { # Standards require redirect URLs to be absolute $this->mRedirect = wfExpandUrl($this->mRedirect, PROTO_CURRENT); $redirect = $this->mRedirect; $code = $this->mRedirectCode; if (wfRunHooks("BeforePageRedirect", array($this, &$redirect, &$code))) { if ($code == '301' || $code == '303') { if (!$wgDebugRedirects) { $message = HttpStatus::getMessage($code); $response->header("HTTP/1.1 {$code} {$message}"); } $this->mLastModified = wfTimestamp(TS_RFC2822); } if ($wgVaryOnXFP) { $this->addVaryHeader('X-Forwarded-Proto'); } $this->sendCacheControl(); $response->header("Content-Type: text/html; charset=utf-8"); if ($wgDebugRedirects) { $url = htmlspecialchars($redirect); print "<html>\n<head>\n<title>Redirect</title>\n</head>\n<body>\n"; print "<p>Location: <a href=\"{$url}\">{$url}</a></p>\n"; print "</body>\n</html>\n"; } else { $response->header('Location: ' . $redirect); } } wfProfileOut(__METHOD__); return; } elseif ($this->mStatusCode) { $message = HttpStatus::getMessage($this->mStatusCode); if ($message) { $response->header('HTTP/1.1 ' . $this->mStatusCode . ' ' . $message); } } # Buffer output; final headers may depend on later processing ob_start(); $response->header("Content-type: {$wgMimeType}; charset=UTF-8"); $response->header('Content-language: ' . $wgLanguageCode); // Prevent framing, if requested $frameOptions = $this->getFrameOptions(); if ($frameOptions) { $response->header("X-Frame-Options: {$frameOptions}"); } if ($this->mArticleBodyOnly) { echo $this->mBodytext; } else { $sk = $this->getSkin(); // add skin specific modules $modules = $sk->getDefaultModules(); // enforce various default modules for all skins $coreModules = array('mediawiki.page.startup', 'mediawiki.user'); // Support for high-density display images if enabled if ($wgResponsiveImages) { $coreModules[] = 'mediawiki.hidpi'; } $this->addModules($coreModules); foreach ($modules as $group) { $this->addModules($group); } MWDebug::addModules($this); if ($wgUseAjax) { // FIXME: deprecate? - not clear why this is useful wfRunHooks('AjaxAddScript', array(&$this)); } // Hook that allows last minute changes to the output page, e.g. // adding of CSS or Javascript by extensions. wfRunHooks('BeforePageDisplay', array(&$this, &$sk)); wfProfileIn('Output-skin'); $sk->outputPage(); wfProfileOut('Output-skin'); } // This hook allows last minute changes to final overall output by modifying output buffer wfRunHooks('AfterFinalPageOutput', array($this)); $this->sendCacheControl(); ob_end_flush(); wfProfileOut(__METHOD__); }
/** * Execute and profile the query. This is a wrapper for capturing timing information * while executing a query. * * @param string $sql the query * @param string $fname the function name * @param bool $isMaster is this against the master * * @return ResultWrapper|resource|bool see doQuery */ protected function executeAndProfileQuery($sql, $fname, $isMaster) { $queryId = MWDebug::query($sql, $fname, $isMaster); $start = microtime(true); // Wikia change: DatabaseMysql returns a resource instead of ResultWrapper instance /* @var $ret resource|bool */ $ret = $this->doQuery($sql); $this->logSql($sql, $ret, $fname, microtime(true) - $start, $isMaster); MWDebug::queryTime($queryId); return $ret; }
} $wgSessionsInObjectCache = true; if ($wgPHPSessionHandling !== 'enable' && $wgPHPSessionHandling !== 'warn' && $wgPHPSessionHandling !== 'disable') { $wgPHPSessionHandling = 'warn'; } if (defined('MW_NO_SESSION')) { // If the entry point wants no session, force 'disable' here unless they // specifically set it to the (undocumented) 'warn'. $wgPHPSessionHandling = MW_NO_SESSION === 'warn' ? 'warn' : 'disable'; } Profiler::instance()->scopedProfileOut($ps_default); // Disable MWDebug for command line mode, this prevents MWDebug from eating up // all the memory from logging SQL queries on maintenance scripts global $wgCommandLineMode; if ($wgDebugToolbar && !$wgCommandLineMode) { MWDebug::init(); } if (!class_exists('AutoLoader')) { require_once "{$IP}/includes/AutoLoader.php"; } // Reset the global service locator, so any services that have already been created will be // re-created while taking into account any custom settings and extensions. MediaWikiServices::resetGlobalInstance(new GlobalVarConfig(), 'quick'); if ($wgSharedDB && $wgSharedTables) { // Apply $wgSharedDB table aliases for the local LB (all non-foreign DB connections) MediaWikiServices::getInstance()->getDBLoadBalancer()->setTableAliases(array_fill_keys($wgSharedTables, ['dbname' => $wgSharedDB, 'schema' => $wgSharedSchema, 'prefix' => $wgSharedPrefix])); } // Define a constant that indicates that the bootstrapping of the service locator // is complete. define('MW_SERVICE_BOOTSTRAP_COMPLETE', 1); // Install a header callback to prevent caching of responses with cookies (T127993)
/** * Run an SQL query and return the result. Normally throws a DBQueryError * on failure. If errors are ignored, returns false instead. * * In new code, the query wrappers select(), insert(), update(), delete(), * etc. should be used where possible, since they give much better DBMS * independence and automatically quote or validate user input in a variety * of contexts. This function is generally only useful for queries which are * explicitly DBMS-dependent and are unsupported by the query wrappers, such * as CREATE TABLE. * * However, the query wrappers themselves should call this function. * * @param string $sql SQL query * @param string $fname Name of the calling function, for profiling/SHOW PROCESSLIST * comment (you can use __METHOD__ or add some extra info) * @param bool $tempIgnore Whether to avoid throwing an exception on errors... * maybe best to catch the exception instead? * @throws MWException * @return bool|ResultWrapper True for a successful write query, ResultWrapper object * for a successful read query, or false on failure if $tempIgnore set */ public function query($sql, $fname = __METHOD__, $tempIgnore = false) { global $wgUser; $this->mLastQuery = $sql; $isWriteQuery = $this->isWriteQuery($sql); if ($isWriteQuery) { $reason = $this->getReadOnlyReason(); if ($reason !== false) { throw new DBReadOnlyError($this, "Database is read-only: {$reason}"); } # Set a flag indicating that writes have been done $this->mDoneWrites = microtime(true); } # Add a comment for easy SHOW PROCESSLIST interpretation if (is_object($wgUser) && $wgUser->isItemLoaded('name')) { $userName = $wgUser->getName(); if (mb_strlen($userName) > 15) { $userName = mb_substr($userName, 0, 15) . '...'; } $userName = str_replace('/', '', $userName); } else { $userName = ''; } // Add trace comment to the begin of the sql string, right after the operator. // Or, for one-word queries (like "BEGIN" or COMMIT") add it to the end (bug 42598) $commentedSql = preg_replace('/\\s|$/', " /* {$fname} {$userName} */ ", $sql, 1); if (!$this->mTrxLevel && $this->getFlag(DBO_TRX) && $this->isTransactableQuery($sql)) { $this->begin(__METHOD__ . " ({$fname})"); $this->mTrxAutomatic = true; } # Keep track of whether the transaction has write queries pending if ($this->mTrxLevel && !$this->mTrxDoneWrites && $isWriteQuery) { $this->mTrxDoneWrites = true; $this->getTransactionProfiler()->transactionWritingIn($this->mServer, $this->mDBname, $this->mTrxShortId); } $isMaster = !is_null($this->getLBInfo('master')); # generalizeSQL will probably cut down the query to reasonable # logging size most of the time. The substr is really just a sanity check. if ($isMaster) { $queryProf = 'query-m: ' . substr(DatabaseBase::generalizeSQL($sql), 0, 255); $totalProf = 'DatabaseBase::query-master'; } else { $queryProf = 'query: ' . substr(DatabaseBase::generalizeSQL($sql), 0, 255); $totalProf = 'DatabaseBase::query'; } # Include query transaction state $queryProf .= $this->mTrxShortId ? " [TRX#{$this->mTrxShortId}]" : ""; $profiler = Profiler::instance(); if (!$profiler instanceof ProfilerStub) { $totalProfSection = $profiler->scopedProfileIn($totalProf); $queryProfSection = $profiler->scopedProfileIn($queryProf); } if ($this->debug()) { wfDebugLog('queries', sprintf("%s: %s", $this->mDBname, $sql)); } $queryId = MWDebug::query($sql, $fname, $isMaster); # Avoid fatals if close() was called $this->assertOpen(); # Do the query and handle errors $startTime = microtime(true); $ret = $this->doQuery($commentedSql); $queryRuntime = microtime(true) - $startTime; # Log the query time and feed it into the DB trx profiler $this->getTransactionProfiler()->recordQueryCompletion($queryProf, $startTime, $isWriteQuery, $this->affectedRows()); MWDebug::queryTime($queryId); # Try reconnecting if the connection was lost if (false === $ret && $this->wasErrorReissuable()) { # Transaction is gone, like it or not $hadTrx = $this->mTrxLevel; // possible lost transaction $this->mTrxLevel = 0; $this->mTrxIdleCallbacks = array(); // bug 65263 $this->mTrxPreCommitCallbacks = array(); // bug 65263 wfDebug("Connection lost, reconnecting...\n"); # Stash the last error values since ping() might clear them $lastError = $this->lastError(); $lastErrno = $this->lastErrno(); if ($this->ping()) { wfDebug("Reconnected\n"); $server = $this->getServer(); $msg = __METHOD__ . ": lost connection to {$server}; reconnected"; wfDebugLog('DBPerformance', "{$msg}:\n" . wfBacktrace(true)); if ($hadTrx) { # Leave $ret as false and let an error be reported. # Callers may catch the exception and continue to use the DB. $this->reportQueryError($lastError, $lastErrno, $sql, $fname, $tempIgnore); } else { # Should be safe to silently retry (no trx and thus no callbacks) $startTime = microtime(true); $ret = $this->doQuery($commentedSql); $queryRuntime = microtime(true) - $startTime; # Log the query time and feed it into the DB trx profiler $this->getTransactionProfiler()->recordQueryCompletion($queryProf, $startTime, $isWriteQuery, $this->affectedRows()); } } else { wfDebug("Failed\n"); } } if (false === $ret) { $this->reportQueryError($this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore); } $res = $this->resultObject($ret); // Destroy profile sections in the opposite order to their creation ScopedCallback::consume($queryProfSection); ScopedCallback::consume($totalProfSection); if ($isWriteQuery && $this->mTrxLevel) { $this->mTrxWriteDuration += $queryRuntime; } return $res; }
/** * Run an SQL query and return the result. Normally throws a DBQueryError * on failure. If errors are ignored, returns false instead. * * In new code, the query wrappers select(), insert(), update(), delete(), * etc. should be used where possible, since they give much better DBMS * independence and automatically quote or validate user input in a variety * of contexts. This function is generally only useful for queries which are * explicitly DBMS-dependent and are unsupported by the query wrappers, such * as CREATE TABLE. * * However, the query wrappers themselves should call this function. * * @param $sql String: SQL query * @param $fname String: Name of the calling function, for profiling/SHOW PROCESSLIST * comment (you can use __METHOD__ or add some extra info) * @param $tempIgnore Boolean: Whether to avoid throwing an exception on errors... * maybe best to catch the exception instead? * @throws MWException * @return boolean|ResultWrapper. true for a successful write query, ResultWrapper object * for a successful read query, or false on failure if $tempIgnore set */ public function query( $sql, $fname = __METHOD__, $tempIgnore = false ) { global $wgUser, $wgDebugDBTransactions; $this->mLastQuery = $sql; if ( !$this->mDoneWrites && $this->isWriteQuery( $sql ) ) { # Set a flag indicating that writes have been done wfDebug( __METHOD__ . ": Writes done: $sql\n" ); $this->mDoneWrites = true; } # Add a comment for easy SHOW PROCESSLIST interpretation if ( is_object( $wgUser ) && $wgUser->isItemLoaded( 'name' ) ) { $userName = $wgUser->getName(); if ( mb_strlen( $userName ) > 15 ) { $userName = mb_substr( $userName, 0, 15 ) . '...'; } $userName = str_replace( '/', '', $userName ); } else { $userName = ''; } // Add trace comment to the begin of the sql string, right after the operator. // Or, for one-word queries (like "BEGIN" or COMMIT") add it to the end (bug 42598) $commentedSql = preg_replace( '/\s|$/', " /* $fname $userName */ ", $sql, 1 ); # If DBO_TRX is set, start a transaction if ( ( $this->mFlags & DBO_TRX ) && !$this->mTrxLevel && $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 && strpos( $sqlstart, "SET " ) !== 0 ) { if ( $wgDebugDBTransactions ) { wfDebug( "Implicit transaction start.\n" ); } $this->begin( __METHOD__ . " ($fname)" ); $this->mTrxAutomatic = true; } } # Keep track of whether the transaction has write queries pending if ( $this->mTrxLevel && !$this->mTrxDoneWrites && $this->isWriteQuery( $sql ) ) { $this->mTrxDoneWrites = true; Profiler::instance()->transactionWritingIn( $this->mServer, $this->mDBname ); } $isMaster = !is_null( $this->getLBInfo( 'master' ) ); if ( !Profiler::instance()->isStub() ) { # generalizeSQL will probably cut down the query to reasonable # logging size most of the time. The substr is really just a sanity check. if ( $isMaster ) { $queryProf = 'query-m: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 ); $totalProf = 'DatabaseBase::query-master'; } else { $queryProf = 'query: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 ); $totalProf = 'DatabaseBase::query'; } wfProfileIn( $totalProf ); wfProfileIn( $queryProf ); } if ( $this->debug() ) { static $cnt = 0; $cnt++; $sqlx = substr( $commentedSql, 0, 500 ); $sqlx = strtr( $sqlx, "\t\n", ' ' ); $master = $isMaster ? 'master' : 'slave'; wfDebug( "Query {$this->mDBname} ($cnt) ($master): $sqlx\n" ); } $queryId = MWDebug::query( $sql, $fname, $isMaster ); # Do the query and handle errors $ret = $this->doQuery( $commentedSql ); MWDebug::queryTime( $queryId ); # Try reconnecting if the connection was lost if ( false === $ret && $this->wasErrorReissuable() ) { # Transaction is gone, like it or not $this->mTrxLevel = 0; $this->mTrxIdleCallbacks = array(); // cancel $this->mTrxPreCommitCallbacks = array(); // cancel 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 ); if ( $elapsed < 300 ) { # Not a database error to lose a transaction after a minute or two wfLogDBError( "Connection lost and reconnected after {$elapsed}s, query: $sqlx\n" ); } $ret = $this->doQuery( $commentedSql ); } else { wfDebug( "Failed\n" ); } } if ( false === $ret ) { $this->reportQueryError( $this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore ); } if ( !Profiler::instance()->isStub() ) { wfProfileOut( $queryProf ); wfProfileOut( $totalProf ); } return $this->resultObject( $ret ); }
/** * Add the default ResourceLoader modules to this object */ private function addDefaultModules() { global $wgIncludeLegacyJavaScript, $wgPreloadJavaScriptMwUtil, $wgUseAjax, $wgAjaxWatch, $wgEnableMWSuggest; // Add base resources $this->addModules(array('mediawiki.user', 'mediawiki.page.startup', 'mediawiki.page.ready')); if ($wgIncludeLegacyJavaScript) { $this->addModules('mediawiki.legacy.wikibits'); } if ($wgPreloadJavaScriptMwUtil) { $this->addModules('mediawiki.util'); } MWDebug::addModules($this); $skin = $this->getSkin(); // Add various resources if required if ($wgUseAjax) { # macbre: following files are part of merged JS for following skins - don't load them from here $skinName = get_class($skin); $skipWikiaSkins = array(); if (!in_array($skinName, $skipWikiaSkins)) { $this->addModules('mediawiki.legacy.ajax'); } wfRunHooks('AjaxAddScript', array(&$this)); if (!in_array($skinName, $skipWikiaSkins)) { if ($wgAjaxWatch && $this->getUser()->isLoggedIn()) { $this->addModules('mediawiki.action.watch.ajax'); } } if (!in_array($skinName, $skipWikiaSkins)) { if ($wgEnableMWSuggest && !$this->getUser()->getOption('disablesuggest', false)) { $this->addModules('mediawiki.legacy.mwsuggest'); } } } if ($this->getUser()->getBoolOption('editsectiononrightclick')) { $this->addModules('mediawiki.action.view.rightClickEdit'); } # Crazy edit-on-double-click stuff if ($this->isArticle() && $this->getUser()->getOption('editondblclick')) { $this->addModules('mediawiki.action.view.dblClickEdit'); } }
/** * Execute the actual module, without any error handling */ protected function executeAction() { $params = $this->setupExecuteAction(); $module = $this->setupModule(); $this->checkExecutePermissions($module); if (!$this->checkMaxLag($module, $params)) { return; } if (!$this->mInternalMode) { $this->setupExternalResponse($module, $params); } // Execute $module->profileIn(); $module->execute(); wfRunHooks('APIAfterExecute', array(&$module)); $module->profileOut(); if (!$this->mInternalMode) { //append Debug information MWDebug::appendDebugInfoToApiResult($this->getContext(), $this->getResult()); // Print result data $this->printResult(false); } }
/** * Add the default ResourceLoader modules to this object */ private function addDefaultModules() { global $wgIncludeLegacyJavaScript, $wgPreloadJavaScriptMwUtil, $wgUseAjax, $wgAjaxWatch, $wgEnableMWSuggest; // Add base resources $this->addModules(array('mediawiki.user', 'mediawiki.page.startup', 'mediawiki.page.ready')); if ($wgIncludeLegacyJavaScript) { $this->addModules('mediawiki.legacy.wikibits'); } if ($wgPreloadJavaScriptMwUtil) { $this->addModules('mediawiki.util'); } MWDebug::addModules($this); // Add various resources if required if ($wgUseAjax) { $this->addModules('mediawiki.legacy.ajax'); wfRunHooks('AjaxAddScript', array(&$this)); if ($wgAjaxWatch && $this->getUser()->isLoggedIn()) { $this->addModules('mediawiki.action.watch.ajax'); } if ($wgEnableMWSuggest && !$this->getUser()->getOption('disablesuggest', false)) { $this->addModules('mediawiki.legacy.mwsuggest'); } } if ($this->getUser()->getBoolOption('editsectiononrightclick')) { $this->addModules('mediawiki.action.view.rightClickEdit'); } # Crazy edit-on-double-click stuff if ($this->isArticle() && $this->getUser()->getOption('editondblclick')) { $this->addModules('mediawiki.action.view.dblClickEdit'); } }
/** * Run an SQL query and return the result. Normally throws a DBQueryError * on failure. If errors are ignored, returns false instead. * * In new code, the query wrappers select(), insert(), update(), delete(), * etc. should be used where possible, since they give much better DBMS * independence and automatically quote or validate user input in a variety * of contexts. This function is generally only useful for queries which are * explicitly DBMS-dependent and are unsupported by the query wrappers, such * as CREATE TABLE. * * However, the query wrappers themselves should call this function. * * @param $sql String: SQL query * @param $fname String: Name of the calling function, for profiling/SHOW PROCESSLIST * comment (you can use __METHOD__ or add some extra info) * @param $tempIgnore Boolean: Whether to avoid throwing an exception on errors... * maybe best to catch the exception instead? * @return boolean|ResultWrapper. true for a successful write query, ResultWrapper object * for a successful read query, or false on failure if $tempIgnore set * @throws DBQueryError Thrown when the database returns an error of any kind */ public function query($sql, $fname = '', $tempIgnore = false) { $isMaster = !is_null($this->getLBInfo('master')); if (!Profiler::instance()->isStub()) { # generalizeSQL will probably cut down the query to reasonable # logging size most of the time. The substr is really just a sanity check. if ($isMaster) { $queryProf = 'query-m: ' . substr(DatabaseBase::generalizeSQL($sql), 0, 255); $totalProf = 'DatabaseBase::query-master'; } else { $queryProf = 'query: ' . substr(DatabaseBase::generalizeSQL($sql), 0, 255); $totalProf = 'DatabaseBase::query'; } wfProfileIn($totalProf); wfProfileIn($queryProf); } $this->mLastQuery = $sql; if (!$this->mDoneWrites && $this->isWriteQuery($sql)) { # Set a flag indicating that writes have been done wfDebug(__METHOD__ . ": Writes done: {$sql}\n"); $this->mDoneWrites = true; } # Add a comment for easy SHOW PROCESSLIST interpretation global $wgUser; if (is_object($wgUser) && $wgUser->isItemLoaded('name')) { $userName = $wgUser->getName(); if (mb_strlen($userName) > 15) { $userName = mb_substr($userName, 0, 15) . '...'; } $userName = str_replace('/', '', $userName); } else { $userName = ''; } $commentedSql = preg_replace('/\\s/', " /* {$fname} {$userName} */ ", $sql, 1); # If DBO_TRX is set, start a transaction if ($this->mFlags & DBO_TRX && !$this->trxLevel() && $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 && strpos($sqlstart, "SET ") !== 0) { $this->begin(__METHOD__ . " ({$fname})"); } } if ($this->debug()) { static $cnt = 0; $cnt++; $sqlx = substr($commentedSql, 0, 500); $sqlx = strtr($sqlx, "\t\n", ' '); $master = $isMaster ? 'master' : 'slave'; wfDebug("Query {$this->mDBname} ({$cnt}) ({$master}): {$sqlx}\n"); } if (istainted($sql) & TC_MYSQL) { throw new MWException('Tainted query found'); } $queryId = MWDebug::query($sql, $fname, $isMaster); # Do the query and handle errors $ret = $this->doQuery($commentedSql); MWDebug::queryTime($queryId); # Try reconnecting if the connection was lost if (false === $ret && $this->wasErrorReissuable()) { # 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); if ($elapsed < 300) { # Not a database error to lose a transaction after a minute or two wfLogDBError("Connection lost and reconnected after {$elapsed}s, query: {$sqlx}\n"); } $ret = $this->doQuery($commentedSql); } else { wfDebug("Failed\n"); } } if (false === $ret) { $this->reportQueryError($this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore); } if (!Profiler::instance()->isStub()) { wfProfileOut($queryProf); wfProfileOut($totalProf); } return $this->resultObject($ret); }
/** * Returns the HTML to add to the page for the toolbar * * @param $context IContextSource * @return string */ public static function getDebugHTML(IContextSource $context) { if (!self::$enabled) { return ''; } global $wgVersion, $wgRequestTime; MWDebug::log('MWDebug output complete'); $request = $context->getRequest(); $debugInfo = array('mwVersion' => $wgVersion, 'phpVersion' => PHP_VERSION, 'time' => microtime(true) - $wgRequestTime, 'log' => self::$log, 'debugLog' => self::$debug, 'queries' => self::$query, 'request' => array('method' => $_SERVER['REQUEST_METHOD'], 'url' => $request->getRequestURL(), 'headers' => $request->getAllHeaders(), 'params' => $request->getValues()), 'memory' => $context->getLanguage()->formatSize(memory_get_usage()), 'memoryPeak' => $context->getLanguage()->formatSize(memory_get_peak_usage()), 'includes' => self::getFilesIncluded($context)); // Cannot use OutputPage::addJsConfigVars because those are already outputted // by the time this method is called. $html = Html::inlineScript(ResourceLoader::makeLoaderConditionalScript(ResourceLoader::makeConfigSetScript(array('debugInfo' => $debugInfo)))); return $html; }
/** * Append the debug info to given ApiResult * * @param $context IContextSource * @param $result ApiResult */ public static function appendDebugInfoToApiResult(IContextSource $context, ApiResult $result) { if (!self::$enabled) { return; } // output errors as debug info, when display_errors is on // this is necessary for all non html output of the api, because that clears all errors first $obContents = ob_get_contents(); if ($obContents) { $obContentArray = explode('<br />', $obContents); foreach ($obContentArray as $obContent) { if (trim($obContent)) { self::debugMsg(Sanitizer::stripAllTags($obContent)); } } } MWDebug::log('MWDebug output complete'); $debugInfo = self::getDebugInfo($context); $result->setIndexedTagName($debugInfo, 'debuginfo'); $result->setIndexedTagName($debugInfo['log'], 'line'); $result->setIndexedTagName($debugInfo['debugLog'], 'msg'); $result->setIndexedTagName($debugInfo['queries'], 'query'); $result->setIndexedTagName($debugInfo['includes'], 'queries'); $result->addValue(null, 'debuginfo', $debugInfo); }
function printTrail() { echo MWDebug::getDebugHTML($this->getSkin()->getContext()); }
static function onImageBeforeProduceHTML(&$dummy, &$title, &$file, &$frameParams, &$handlerParams, &$time, &$res) { if (!$file) { MWDebug::warning("could not load file from title: {$title}"); return false; } return true; }
private function getNextTip($content) { $showCoachTip = $this->showCoachTip(&$content); $coachTip = null; if ($showCoachTip) { $coachTip = $this->getCoachTip(&$content); MWDebug::log("this will be a coach tip"); } if (!$coachTip) { $this->getDBTip(&$content); } }
/** * Generate debug data HTML for displaying at the bottom of the main content * area. * @return String HTML containing debug data, if enabled (otherwise empty). */ protected function generateDebugHTML() { return MWDebug::getHTMLDebugLog(); }
public function execute($parameters) { global $wgContentTranslationUseMagnusTool, $wgContentTranslationTranslateInTarget; $out = $this->getOutput(); $skin = $this->getSkin(); $request = $this->getRequest(); $user = $this->getUser(); $hasToken = $this->hasToken(); $campaign = $request->getVal('campaign'); $isCampaign = $this->isValidCampaign($campaign); // Direct access, isListed only affects Special:SpecialPages if (!ContentTranslationHooks::isEnabledForUser($user)) { if ($hasToken || $isCampaign) { // User has a token. Enabled cx for the user in this wiki. $this->enableCXBetaFeature(); } else { if ($campaign) { // Show login page if the URL has campaign parameter $out->showPermissionsErrorPage(array(array('badaccess-groups')), 'edit'); return; } $out->showErrorPage('nosuchspecialpage', 'nospecialpagetext'); return; } } // Preloading to avoid FOUC $out->addModuleStyles('ext.cx.header.skin'); if ($hasToken) { $out->addModules('ext.cx.translationview'); // If Wikibase is installed, load the module for linking // the published article with the source article if ($wgContentTranslationTranslateInTarget && defined('WBC_VERSION')) { $out->addModules('ext.cx.wikibase.link'); } } else { $out->addModules('ext.cx.dashboard'); if ($wgContentTranslationUseMagnusTool) { $out->addModules('ext.cx.magnuslink'); } } $this->setHeaders(); $out->setArticleBodyOnly(true); // Default modules copied from OutputPage::addDefaultModules $out->addModules(array('mediawiki.user', 'mediawiki.page.startup', 'mediawiki.page.ready')); // Load legacy modules if any, for the skin. // Some wikis have Common.js scripts that depend on this module. $defaultSkinModules = $skin->getDefaultModules(); $out->addModules($defaultSkinModules['legacy']); Hooks::run('BeforePageDisplay', array(&$out, &$skin)); // T111668: Make sure we generate the personal tools // before we output the head, as extensions may add // things using the PersonalUrls hook. $toolbarList = Html::rawElement('ul', null, $skin->getPersonalToolsList()); $out->addHTML($out->headElement($skin)); $out->addHTML(Html::element('noscript', array(), $this->msg('cx-javascript')->text())); $out->addHtml(MWDebug::getDebugHTML($this->getContext())); $out->addHTML(Html::rawElement('div', array('id' => 'p-personal'), $toolbarList)); $out->addHTML($skin->bottomScripts()); $out->addHTML('</body></html>'); }
/** * Send a warning as a PHP error and the debug log. This is intended for logging * warnings in production. For logging development warnings, use WfWarn instead. * * @param string $msg Message to send * @param int $callerOffset Number of items to go back in the backtrace to * find the correct caller (1 = function calling wfLogWarning, ...) * @param int $level PHP error level; defaults to E_USER_WARNING */ function wfLogWarning($msg, $callerOffset = 1, $level = E_USER_WARNING) { MWDebug::warning($msg, $callerOffset + 1, $level, 'production'); }
static function getNextArticleToPatrolInner($rcid = null) { global $wgRequest, $wgUser, $wgCookiePrefix; $show_namespace = $wgRequest->getVal('namespace'); $invert = $wgRequest->getVal('invert'); $reverse = $wgRequest->getVal('reverse'); $featured = $wgRequest->getVal('featured'); $title = $wgRequest->getVal('target'); $skiptitle = $wgRequest->getVal('skiptitle'); $rc_user_filter = trim(urldecode($wgRequest->getVal('rc_user_filter'))); // assert that current user is not anon if ($wgUser->isAnon()) { return null; } // In English, when a user rolls back an edit, it gives the edit a comment // like: "Reverted edits by ...", so MediaWiki:rollback_comment_prefix // is set to "Reverted" in English wikiHow. $rollbackCommentPrefix = wfMessage('rollback_comment_prefix')->plain(); if (empty($rollbackCommentPrefix) || strpos($rollbackCommentPrefix, '&') === 0) { die("Cannot use RCPatrol feature until MediaWiki:rollback_comment_prefix is set up properly"); } $t = null; if ($title) { $t = Title::newFromText($title); } $skip = null; if ($skiptitle) { $skip = Title::newFromText($skiptitle); } $dbr = wfGetDB(DB_MASTER); /* DEPRECATED rc_moved_to_ns & rc_moved_to_title columns $sql = "SELECT rc_id, rc_cur_id, rc_moved_to_ns, rc_moved_to_title, rc_new, rc_namespace, rc_title, rc_last_oldid, rc_this_oldid FROM recentchanges LEFT OUTER JOIN page ON rc_cur_id = page_id AND rc_namespace = page_namespace WHERE ";*/ $sql = "SELECT rc_id, rc_cur_id, rc_new, rc_namespace, rc_title, rc_last_oldid, rc_this_oldid \n\t\t\tFROM recentchanges \n\t\t\tLEFT OUTER JOIN page ON rc_cur_id = page_id AND rc_namespace = page_namespace \n\t\t\tWHERE "; if (!$wgRequest->getVal('ignore_rcid') && $rcid) { $sql .= " rc_id " . ($reverse == 1 ? " > " : " < ") . " {$rcid} and "; } // if we filter by user we show both patrolled and non-patrolled edits if ($rc_user_filter) { $sql .= " rc_user_text = " . $dbr->addQuotes($rc_user_filter); if ($rcid) { $sql .= " AND rc_id < " . $rcid; } } else { $sql .= " rc_patrolled = 0 "; } // can't patrol your own edits $sql .= " AND rc_user <> " . $wgUser->getID(); // only featured? if ($featured) { $sql .= " AND page_is_featured = 1 "; } if ($show_namespace) { $sql .= " AND rc_namespace " . ($invert ? '<>' : '=') . $show_namespace; } else { // always ignore video $sql .= " AND rc_namespace <> " . NS_VIDEO; } // log entries have namespace = -1, we don't want to show those, hide bots too $sql .= " AND rc_namespace >= 0 AND rc_bot = 0 "; if ($t) { $sql .= " AND rc_title <> " . $dbr->addQuotes($t->getDBKey()); } if ($skip) { $sql .= " AND rc_title <> " . $dbr->addQuotes($skip->getDBKey()); } $sa = $wgRequest->getVal('sa'); if ($sa) { $sa = Title::newFromText($sa); $sql .= " AND rc_title = " . $dbr->addQuotes($sa->getDBKey()); } // has the user skipped any articles? $cookiename = $wgCookiePrefix . "Rcskip"; $skipids = ""; if (isset($_COOKIE[$cookiename])) { $cookie_ids = array_unique(split(",", $_COOKIE[$cookiename])); $ids = array(); //safety first foreach ($cookie_ids as $id) { $id = intval($id); if ($id > 0) { $ids[] = $id; } } if ($ids) { $skipids = " AND rc_cur_id NOT IN (" . implode(",", $ids) . ") "; } } $sql .= "{$skipids} ORDER BY rc_timestamp " . ($reverse == 1 ? "" : "DESC ") . "LIMIT 1"; $res = $dbr->query($sql, __METHOD__); $row = $res->fetchObject(); /*$show=true; if ($show){ var_dump($_GET); var_dump($_POST); echo $sql; var_dump($row); exit; }*/ if ($row) { $result = array(); $t = Title::makeTitle($row->rc_namespace, $row->rc_title); if ($t->isRedirect()) { $wp = new WikiPage($t); $t = $wp->getRedirectTarget(); } // if title has been deleted set $t to null so we will skip it if (!$t->exists()) { MWDebug::log("{$t} does not exist"); $t = null; } $result['rc_cur_id'] = $row->rc_cur_id; if ($rc_user_filter) { $result['rchi'] = $result['rclo'] = $row->rc_id; $result['new'] = $dbr->selectField('recentchanges', array('rc_this_oldid'), array('rc_id' => $row->rc_id)); } else { // always compare to current version $result['new'] = $dbr->selectField('revision', array('max(rev_id)'), array('rev_page' => $row->rc_cur_id)); $result['rchi'] = $dbr->selectField('recentchanges', array('rc_id'), array('rc_this_oldid' => $result['new'])); $result['rclo'] = $dbr->selectField('recentchanges', array('min(rc_id)'), array('rc_patrolled' => 0, "rc_cur_id" => $row->rc_cur_id)); // do we have a reverted edit caught between these 2? // if so, only show the reversion, because otherwise you get the reversion trapped in the middle // and it shows a weird diff page. $hi = isset($result['rchi']) ? $result['rchi'] : $row->rc_id; if ($hi) { $reverted_id = $dbr->selectField('recentchanges', array('min(rc_id)'), array('rc_comment like ' . $dbr->addQuotes($rollbackCommentPrefix . '%'), "rc_id < {$hi}", "rc_id >= {$result['rclo']}", "rc_cur_id" => $row->rc_cur_id)); if ($reverted_id) { $result['rchi'] = $reverted_id; $result['new'] = $dbr->selectField('recentchanges', array('rc_this_oldid'), array('rc_id' => $reverted_id)); $row->rc_id = $result['rchi']; } //} else { // $email = new MailAddress("*****@*****.**"); // $subject = "Could not find hi variable " . date("r"); // $body = print_r($_SERVER, true) . "\n\n" . $sql . "\n\n" . print_r($result, true) . "\n\n\$hi: " . $hi; // UserMailer::send($email, $email, $subject, $body); } if (!$result['rclo']) { $result['rclo'] = $row->rc_id; } if (!$result['rchi']) { $result['rchi'] = $row->rc_id; } // is the last patrolled edit a rollback? if so, show the diff starting at that edit // makes it more clear when someone has reverted vandalism $result['vandal'] = 0; $comm = $dbr->selectField('recentchanges', array('rc_comment'), array('rc_id' => $result['rclo'])); if (strpos($comm, $rollbackCommentPrefix) === 0) { $row2 = $dbr->selectRow('recentchanges', array('rc_id', 'rc_comment'), array("rc_id < {$result['rclo']}", 'rc_cur_id' => $row->rc_cur_id), __METHOD__, array("ORDER BY" => "rc_id desc", "LIMIT" => 1)); if ($row2) { $result['rclo'] = $row2->rc_id; } $result['vandal'] = 1; } } $result['user'] = $dbr->selectField('recentchanges', array('rc_user_text'), array('rc_this_oldid' => $result['new'])); $result['old'] = $dbr->selectField('recentchanges', array('rc_last_oldid'), array('rc_id' => $result['rclo'])); $result['title'] = $t; $result['rcid'] = $row->rc_id; if ($result['rchi'] == $result['rclo']) { $conds = array('rc_id' => $result['rchi']); } else { $conds = array('rc_id <= ' . $result['rchi'], 'rc_id >= ' . $result['rclo']); } $result['count'] = $dbr->selectField('recentchanges', array('count(*)'), array("rc_id <= " . $result['rchi'], "rc_id >= " . $result['rclo'], "rc_patrolled" => 0, "rc_cur_id" => $row->rc_cur_id)); $result = self::getListofEditors($result); return $result; } else { return null; } }
/** * Execute the actual module, without any error handling */ protected function executeAction() { $params = $this->setupExecuteAction(); $module = $this->setupModule(); $this->mModule = $module; $this->checkExecutePermissions($module); if (!$this->checkMaxLag($module, $params)) { return; } if (!$this->checkConditionalRequestHeaders($module)) { return; } if (!$this->mInternalMode) { $this->setupExternalResponse($module, $params); } $this->checkAsserts($params); // Execute $module->execute(); Hooks::run('APIAfterExecute', array(&$module)); $this->reportUnusedParams(); if (!$this->mInternalMode) { // append Debug information MWDebug::appendDebugInfoToApiResult($this->getContext(), $this->getResult()); // Print result data $this->printResult(false); } }
/** * Run an SQL query and return the result. Normally throws a DBQueryError * on failure. If errors are ignored, returns false instead. * * In new code, the query wrappers select(), insert(), update(), delete(), * etc. should be used where possible, since they give much better DBMS * independence and automatically quote or validate user input in a variety * of contexts. This function is generally only useful for queries which are * explicitly DBMS-dependent and are unsupported by the query wrappers, such * as CREATE TABLE. * * However, the query wrappers themselves should call this function. * * @param string $sql SQL query * @param string $fname Name of the calling function, for profiling/SHOW PROCESSLIST * comment (you can use __METHOD__ or add some extra info) * @param bool $tempIgnore Whether to avoid throwing an exception on errors... * maybe best to catch the exception instead? * @throws MWException * @return bool|ResultWrapper True for a successful write query, ResultWrapper object * for a successful read query, or false on failure if $tempIgnore set */ public function query($sql, $fname = __METHOD__, $tempIgnore = false) { global $wgUser, $wgDebugDBTransactions, $wgDebugDumpSqlLength; $this->mLastQuery = $sql; if ($this->isWriteQuery($sql)) { # Set a flag indicating that writes have been done wfDebug(__METHOD__ . ': Writes done: ' . DatabaseBase::generalizeSQL($sql) . "\n"); $this->mDoneWrites = microtime(true); } # Add a comment for easy SHOW PROCESSLIST interpretation if (is_object($wgUser) && $wgUser->isItemLoaded('name')) { $userName = $wgUser->getName(); if (mb_strlen($userName) > 15) { $userName = mb_substr($userName, 0, 15) . '...'; } $userName = str_replace('/', '', $userName); } else { $userName = ''; } // Add trace comment to the begin of the sql string, right after the operator. // Or, for one-word queries (like "BEGIN" or COMMIT") add it to the end (bug 42598) $commentedSql = preg_replace('/\\s|$/', " /* {$fname} {$userName} */ ", $sql, 1); # If DBO_TRX is set, start a transaction if ($this->mFlags & DBO_TRX && !$this->mTrxLevel && $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 && strpos($sqlstart, "SET ") !== 0) { if ($wgDebugDBTransactions) { wfDebug("Implicit transaction start.\n"); } $this->begin(__METHOD__ . " ({$fname})"); $this->mTrxAutomatic = true; } } # Keep track of whether the transaction has write queries pending if ($this->mTrxLevel && !$this->mTrxDoneWrites && $this->isWriteQuery($sql)) { $this->mTrxDoneWrites = true; Profiler::instance()->transactionWritingIn($this->mServer, $this->mDBname, $this->mTrxShortId); } $queryProf = ''; $totalProf = ''; $isMaster = !is_null($this->getLBInfo('master')); if (!Profiler::instance()->isStub()) { # generalizeSQL will probably cut down the query to reasonable # logging size most of the time. The substr is really just a sanity check. if ($isMaster) { $queryProf = 'query-m: ' . substr(DatabaseBase::generalizeSQL($sql), 0, 255); $totalProf = 'DatabaseBase::query-master'; } else { $queryProf = 'query: ' . substr(DatabaseBase::generalizeSQL($sql), 0, 255); $totalProf = 'DatabaseBase::query'; } # Include query transaction state $queryProf .= $this->mTrxShortId ? " [TRX#{$this->mTrxShortId}]" : ""; $trx = $this->mTrxLevel ? 'TRX=yes' : 'TRX=no'; wfProfileIn($totalProf); wfProfileIn($queryProf); } if ($this->debug()) { static $cnt = 0; $cnt++; $sqlx = $wgDebugDumpSqlLength ? substr($commentedSql, 0, $wgDebugDumpSqlLength) : $commentedSql; $sqlx = strtr($sqlx, "\t\n", ' '); $master = $isMaster ? 'master' : 'slave'; wfDebug("Query {$this->mDBname} ({$cnt}) ({$master}): {$sqlx}\n"); } $queryId = MWDebug::query($sql, $fname, $isMaster); # Avoid fatals if close() was called if (!$this->isOpen()) { throw new DBUnexpectedError($this, "DB connection was already closed."); } # Do the query and handle errors $ret = $this->doQuery($commentedSql); MWDebug::queryTime($queryId); # Try reconnecting if the connection was lost if (false === $ret && $this->wasErrorReissuable()) { # Transaction is gone, like it or not $hadTrx = $this->mTrxLevel; // possible lost transaction $this->mTrxLevel = 0; $this->mTrxIdleCallbacks = array(); // bug 65263 $this->mTrxPreCommitCallbacks = array(); // bug 65263 wfDebug("Connection lost, reconnecting...\n"); # Stash the last error values since ping() might clear them $lastError = $this->lastError(); $lastErrno = $this->lastErrno(); if ($this->ping()) { global $wgRequestTime; wfDebug("Reconnected\n"); $sqlx = $wgDebugDumpSqlLength ? substr($commentedSql, 0, $wgDebugDumpSqlLength) : $commentedSql; $sqlx = strtr($sqlx, "\t\n", ' '); $elapsed = round(microtime(true) - $wgRequestTime, 3); if ($elapsed < 300) { # Not a database error to lose a transaction after a minute or two wfLogDBError("Connection lost and reconnected after {$elapsed}s, query: {$sqlx}"); } if ($hadTrx) { # Leave $ret as false and let an error be reported. # Callers may catch the exception and continue to use the DB. $this->reportQueryError($lastError, $lastErrno, $sql, $fname, $tempIgnore); } else { # Should be safe to silently retry (no trx and thus no callbacks) $ret = $this->doQuery($commentedSql); } } else { wfDebug("Failed\n"); } } if (false === $ret) { $this->reportQueryError($this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore); } if (!Profiler::instance()->isStub()) { wfProfileOut($queryProf); wfProfileOut($totalProf); } return $this->resultObject($ret); }
/** * Finally, all the text has been munged and accumulated into * the object, let's actually output it: */ public function output() { if ($this->mDoNothing) { return; } $response = $this->getRequest()->response(); $config = $this->getConfig(); if ($this->mRedirect != '') { # Standards require redirect URLs to be absolute $this->mRedirect = wfExpandUrl($this->mRedirect, PROTO_CURRENT); $redirect = $this->mRedirect; $code = $this->mRedirectCode; if (Hooks::run("BeforePageRedirect", array($this, &$redirect, &$code))) { if ($code == '301' || $code == '303') { if (!$config->get('DebugRedirects')) { $response->statusHeader($code); } $this->mLastModified = wfTimestamp(TS_RFC2822); } if ($config->get('VaryOnXFP')) { $this->addVaryHeader('X-Forwarded-Proto'); } $this->sendCacheControl(); $response->header("Content-Type: text/html; charset=utf-8"); if ($config->get('DebugRedirects')) { $url = htmlspecialchars($redirect); print "<html>\n<head>\n<title>Redirect</title>\n</head>\n<body>\n"; print "<p>Location: <a href=\"{$url}\">{$url}</a></p>\n"; print "</body>\n</html>\n"; } else { $response->header('Location: ' . $redirect); } } return; } elseif ($this->mStatusCode) { $response->statusHeader($this->mStatusCode); } # Buffer output; final headers may depend on later processing ob_start(); $response->header('Content-type: ' . $config->get('MimeType') . '; charset=UTF-8'); $response->header('Content-language: ' . $config->get('LanguageCode')); // Avoid Internet Explorer "compatibility view" in IE 8-10, so that // jQuery etc. can work correctly. $response->header('X-UA-Compatible: IE=Edge'); // Prevent framing, if requested $frameOptions = $this->getFrameOptions(); if ($frameOptions) { $response->header("X-Frame-Options: {$frameOptions}"); } if ($this->mArticleBodyOnly) { echo $this->mBodytext; } else { $sk = $this->getSkin(); // add skin specific modules $modules = $sk->getDefaultModules(); // enforce various default modules for all skins $coreModules = array('mediawiki.page.startup', 'mediawiki.user'); // Support for high-density display images if enabled if ($config->get('ResponsiveImages')) { $coreModules[] = 'mediawiki.hidpi'; } $this->addModules($coreModules); foreach ($modules as $group) { $this->addModules($group); } MWDebug::addModules($this); // Hook that allows last minute changes to the output page, e.g. // adding of CSS or Javascript by extensions. Hooks::run('BeforePageDisplay', array(&$this, &$sk)); $sk->outputPage(); } // This hook allows last minute changes to final overall output by modifying output buffer Hooks::run('AfterFinalPageOutput', array($this)); $this->sendCacheControl(); ob_end_flush(); }
/** * Send a warning either to the debug log or in a PHP error depending on * $wgDevelopmentWarnings * * @param $msg String: message to send * @param $callerOffset Integer: number of items to go back in the backtrace to * find the correct caller (1 = function calling wfWarn, ...) * @param $level Integer: PHP error level; only used when $wgDevelopmentWarnings * is true */ function wfWarn($msg, $callerOffset = 1, $level = E_USER_NOTICE) { MWDebug::warning($msg, $callerOffset + 1, $level); }