Exemplo n.º 1
0
/**
 * Stop profiling of a function
 * @param $functionname String: name of the function we have profiled
 */
function wfProfileOut($functionname = 'missing')
{
    global $wgProfiler;
    if ($wgProfiler instanceof Profiler || isset($wgProfiler['class'])) {
        Profiler::instance()->profileOut($functionname);
    }
}
 /**
  * Override the default Symphony constructor to initialise the Log, Config
  * and Database objects for installation/update. This allows us to use the
  * normal accessors.
  */
 protected function __construct()
 {
     self::$Profiler = Profiler::instance();
     self::$Profiler->sample('Engine Initialisation');
     if (get_magic_quotes_gpc()) {
         General::cleanArray($_SERVER);
         General::cleanArray($_COOKIE);
         General::cleanArray($_GET);
         General::cleanArray($_POST);
     }
     // Include the default Config for installation.
     include INSTALL . '/includes/config_default.php';
     static::initialiseConfiguration($settings);
     // Initialize date/time
     define_safe('__SYM_DATE_FORMAT__', self::Configuration()->get('date_format', 'region'));
     define_safe('__SYM_TIME_FORMAT__', self::Configuration()->get('time_format', 'region'));
     define_safe('__SYM_DATETIME_FORMAT__', __SYM_DATE_FORMAT__ . self::Configuration()->get('datetime_separator', 'region') . __SYM_TIME_FORMAT__);
     DateTimeObj::setSettings(self::Configuration()->get('region'));
     // Initialize Language, Logs and Database
     static::initialiseLang();
     static::initialiseLog(INSTALL_LOGS . '/install');
     static::initialiseDatabase();
     // Initialize error handlers
     GenericExceptionHandler::initialise(Symphony::Log());
     GenericErrorHandler::initialise(Symphony::Log());
 }
Exemplo n.º 3
0
 /**
  * purge is slightly weird because it can be either formed or formless depending
  * on user permissions
  */
 public function show()
 {
     $this->setHeaders();
     // This will throw exceptions if there's a problem
     $this->checkCanExecute($this->getUser());
     $user = $this->getUser();
     if ($user->pingLimiter('purge')) {
         // TODO: Display actionthrottledtext
         return;
     }
     if ($user->isAllowed('purge')) {
         // This will update the database immediately, even on HTTP GET.
         // Lots of uses may exist for this feature, so just ignore warnings.
         Profiler::instance()->getTransactionProfiler()->resetExpectations();
         $this->redirectParams = wfArrayToCgi(array_diff_key($this->getRequest()->getQueryValues(), ['title' => null, 'action' => null]));
         if ($this->onSubmit([])) {
             $this->onSuccess();
         }
     } else {
         $this->redirectParams = $this->getRequest()->getVal('redirectparams', '');
         $form = $this->getForm();
         if ($form->show()) {
             $this->onSuccess();
         }
     }
 }
Exemplo n.º 4
0
 /**
  * Main execution point
  *
  * @param null|string $code Confirmation code passed to the page
  * @throws PermissionsError
  * @throws ReadOnlyError
  * @throws UserNotLoggedIn
  */
 function execute($code)
 {
     // Ignore things like master queries/connections on GET requests.
     // It's very convenient to just allow formless link usage.
     $trxProfiler = Profiler::instance()->getTransactionProfiler();
     $this->setHeaders();
     $this->checkReadOnly();
     $this->checkPermissions();
     // This could also let someone check the current email address, so
     // require both permissions.
     if (!$this->getUser()->isAllowed('viewmyprivateinfo')) {
         throw new PermissionsError('viewmyprivateinfo');
     }
     if ($code === null || $code === '') {
         $this->requireLogin('confirmemail_needlogin');
         if (Sanitizer::validateEmail($this->getUser()->getEmail())) {
             $this->showRequestForm();
         } else {
             $this->getOutput()->addWikiMsg('confirmemail_noemail');
         }
     } else {
         $old = $trxProfiler->setSilenced(true);
         $this->attemptConfirm($code);
         $trxProfiler->setSilenced($old);
     }
 }
Exemplo n.º 5
0
 /**
  * Get singleton instance
  * @return Profiler
  */
 public static function getInstance()
 {
     if (!isset(self::$instance)) {
         self::$instance = new Profiler();
     }
     return self::$instance;
 }
Exemplo n.º 6
0
 /**
  * The Symphony constructor initialises the class variables of Symphony.
  * It will set the DateTime settings, define new date constants and initialise
  * the correct Language for the currently logged in Author. If magic quotes
  * are enabled, Symphony will sanitize the `$_SERVER`, `$_COOKIE`,
  * `$_GET` and `$_POST` arrays. The constructor loads in
  * the initial Configuration values from the `CONFIG` file
  */
 protected function __construct()
 {
     self::$Profiler = Profiler::instance();
     if (get_magic_quotes_gpc()) {
         General::cleanArray($_SERVER);
         General::cleanArray($_COOKIE);
         General::cleanArray($_GET);
         General::cleanArray($_POST);
     }
     // Set date format throughout the system
     define_safe('__SYM_DATE_FORMAT__', self::Configuration()->get('date_format', 'region'));
     define_safe('__SYM_TIME_FORMAT__', self::Configuration()->get('time_format', 'region'));
     define_safe('__SYM_DATETIME_FORMAT__', __SYM_DATE_FORMAT__ . self::Configuration()->get('datetime_separator', 'region') . __SYM_TIME_FORMAT__);
     DateTimeObj::setSettings(self::Configuration()->get('region'));
     self::initialiseErrorHandler();
     // Initialize language management
     Lang::initialize();
     Lang::set(self::$Configuration->get('lang', 'symphony'));
     self::initialiseCookie();
     // If the user is not a logged in Author, turn off the verbose error messages.
     if (!self::isLoggedIn() && is_null(self::$Author)) {
         GenericExceptionHandler::$enabled = false;
     }
     // Engine is ready.
     self::$Profiler->sample('Engine Initialisation');
 }
Exemplo n.º 7
0
 /**
  * Construct a factory based on a configuration array (typically from $wgLBFactoryConf)
  * @param array $conf
  */
 public function __construct(array $conf)
 {
     if (isset($conf['readOnlyReason']) && is_string($conf['readOnlyReason'])) {
         $this->readOnlyReason = $conf['readOnlyReason'];
     }
     $this->chronProt = $this->newChronologyProtector();
     $this->trxProfiler = Profiler::instance()->getTransactionProfiler();
 }
Exemplo n.º 8
0
	/**
	 * Begin profiling of a function and return an object that ends profiling of
	 * the function when that object leaves scope. As long as the object is not
	 * specifically linked to other objects, it will fall out of scope at the same
	 * moment that the function to be profiled terminates.
	 *
	 * This is typically called like:
	 * <code>$section = new ProfileSection( __METHOD__ );</code>
	 *
	 * @param string $name Name of the function to profile
	 */
	public function __construct( $name ) {
		$this->name = $name;
		if ( Profiler::$__instance === null ) { // use this directly to reduce overhead
			Profiler::instance();
		}
		if ( !( Profiler::$__instance instanceof ProfilerStub ) ) {
			$this->enabled = true;
			Profiler::$__instance->profileIn( $this->name );
		}
	}
 function execute($code)
 {
     // Ignore things like master queries/connections on GET requests.
     // It's very convenient to just allow formless link usage.
     Profiler::instance()->getTransactionProfiler()->resetExpectations();
     $this->setHeaders();
     $this->checkReadOnly();
     $this->checkPermissions();
     $this->attemptInvalidate($code);
 }
Exemplo n.º 10
0
 /**
  * @param array $lbConf Config for LBFactory::__construct()
  * @param Config $mainConfig Main config object from MediaWikiServices
  * @return array
  */
 public static function applyDefaultConfig(array $lbConf, Config $mainConfig)
 {
     global $wgCommandLineMode;
     $lbConf += ['localDomain' => new DatabaseDomain($mainConfig->get('DBname'), null, $mainConfig->get('DBprefix')), 'profiler' => Profiler::instance(), 'trxProfiler' => Profiler::instance()->getTransactionProfiler(), 'replLogger' => LoggerFactory::getInstance('DBReplication'), 'queryLogger' => LoggerFactory::getInstance('DBQuery'), 'connLogger' => LoggerFactory::getInstance('DBConnection'), 'perfLogger' => LoggerFactory::getInstance('DBPerformance'), 'errorLogger' => [MWExceptionHandler::class, 'logException'], 'cliMode' => $wgCommandLineMode, 'hostname' => wfHostname(), 'readOnlyReason' => wfConfiguredReadOnlyReason()];
     if ($lbConf['class'] === 'LBFactorySimple') {
         if (isset($lbConf['servers'])) {
             // Server array is already explicitly configured; leave alone
         } elseif (is_array($mainConfig->get('DBservers'))) {
             foreach ($mainConfig->get('DBservers') as $i => $server) {
                 if ($server['type'] === 'sqlite') {
                     $server += ['dbDirectory' => $mainConfig->get('SQLiteDataDir')];
                 } elseif ($server['type'] === 'postgres') {
                     $server += ['port' => $mainConfig->get('DBport')];
                 }
                 $lbConf['servers'][$i] = $server + ['schema' => $mainConfig->get('DBmwschema'), 'tablePrefix' => $mainConfig->get('DBprefix'), 'flags' => DBO_DEFAULT, 'sqlMode' => $mainConfig->get('SQLMode'), 'utf8Mode' => $mainConfig->get('DBmysql5')];
             }
         } else {
             $flags = DBO_DEFAULT;
             $flags |= $mainConfig->get('DebugDumpSql') ? DBO_DEBUG : 0;
             $flags |= $mainConfig->get('DBssl') ? DBO_SSL : 0;
             $flags |= $mainConfig->get('DBcompress') ? DBO_COMPRESS : 0;
             $server = ['host' => $mainConfig->get('DBserver'), 'user' => $mainConfig->get('DBuser'), 'password' => $mainConfig->get('DBpassword'), 'dbname' => $mainConfig->get('DBname'), 'schema' => $mainConfig->get('DBmwschema'), 'tablePrefix' => $mainConfig->get('DBprefix'), 'type' => $mainConfig->get('DBtype'), 'load' => 1, 'flags' => $flags, 'sqlMode' => $mainConfig->get('SQLMode'), 'utf8Mode' => $mainConfig->get('DBmysql5')];
             if ($server['type'] === 'sqlite') {
                 $server['dbDirectory'] = $mainConfig->get('SQLiteDataDir');
             } elseif ($server['type'] === 'postgres') {
                 $server['port'] = $mainConfig->get('DBport');
             }
             $lbConf['servers'] = [$server];
         }
         if (!isset($lbConf['externalClusters'])) {
             $lbConf['externalClusters'] = $mainConfig->get('ExternalServers');
         }
     } elseif ($lbConf['class'] === 'LBFactoryMulti') {
         if (isset($lbConf['serverTemplate'])) {
             $lbConf['serverTemplate']['schema'] = $mainConfig->get('DBmwschema');
             $lbConf['serverTemplate']['sqlMode'] = $mainConfig->get('SQLMode');
             $lbConf['serverTemplate']['utf8Mode'] = $mainConfig->get('DBmysql5');
         }
     }
     // Use APC/memcached style caching, but avoids loops with CACHE_DB (T141804)
     $sCache = MediaWikiServices::getInstance()->getLocalServerObjectCache();
     if ($sCache->getQoS($sCache::ATTR_EMULATION) > $sCache::QOS_EMULATION_SQL) {
         $lbConf['srvCache'] = $sCache;
     }
     $cCache = ObjectCache::getLocalClusterInstance();
     if ($cCache->getQoS($cCache::ATTR_EMULATION) > $cCache::QOS_EMULATION_SQL) {
         $lbConf['memCache'] = $cCache;
     }
     $wCache = MediaWikiServices::getInstance()->getMainWANObjectCache();
     if ($wCache->getQoS($wCache::ATTR_EMULATION) > $wCache::QOS_EMULATION_SQL) {
         $lbConf['wanCache'] = $wCache;
     }
     return $lbConf;
 }
Exemplo n.º 11
0
 public function execute()
 {
     $backend = FileBackendGroup::singleton()->get($this->getOption('b1'));
     $this->doPerfTest($backend);
     if ($this->getOption('b2')) {
         $backend = FileBackendGroup::singleton()->get($this->getOption('b2'));
         $this->doPerfTest($backend);
     }
     $profiler = Profiler::instance();
     $profiler->setTemplated(true);
     //NOTE: as of MW1.21, $profiler->logData() is called implicitly by doMaintenance.php.
 }
Exemplo n.º 12
0
/**
 * Stop profiling of a function
 * @param $functionname String: name of the function we have profiled
 */
function wfProfileOut($functionname = 'missing')
{
    global $wgProfiler;
    // Wikia change - @author: wladek - 2x faster
    if ($wgProfiler instanceof Profiler) {
        if (!$wgProfiler instanceof ProfilerStub) {
            $wgProfiler->profileOut($functionname);
        }
    } elseif (isset($wgProfiler['class'])) {
        Profiler::instance()->profileOut($functionname);
    }
}
Exemplo n.º 13
0
 /**
  * Return a \Profiler instance
  *
  * @note Profiler::$__instance only made public in 1.22 therefore
  * we use our own static to keep overhead at a minimum
  *
  * @see $wgProfiler
  * @see http://www.mediawiki.org/wiki/Profiling#Profiling
  *
  * @since 1.9
  *
  * @return \Profiler|null
  */
 public static function getInstance()
 {
     // Nothing we can do to avoid the global state here until we have
     // public access to Profiler::$__instance
     $profiler = isset($GLOBALS['wgProfiler']['class']);
     if (self::$instance === null && $profiler) {
         self::$instance = \Profiler::instance();
     }
     if (!$profiler) {
         self::reset();
     }
     return self::$instance;
 }
Exemplo n.º 14
0
 public function execute()
 {
     $backend = FileBackendGroup::singleton()->get($this->getOption('b1'));
     $this->doPerfTest($backend);
     if ($this->getOption('b2')) {
         $backend = FileBackendGroup::singleton()->get($this->getOption('b2'));
         $this->doPerfTest($backend);
     }
     $profiler = Profiler::instance();
     $profiler->setTemplated(true);
     $profiler->logData();
     // prints
 }
Exemplo n.º 15
0
 /**
  * Overridden to make changes to resource loader
  *
  * @param null|OutputPage $out
  */
 function outputPage(OutputPage $out = null)
 {
     global $wgScript, $wgMobileFrontend2Logo;
     $out = $this->getOutput();
     $request = $this->getRequest();
     $user = $this->getUser();
     $title = $this->getTitle();
     // We need to disable all the default RL modules, do that like this
     $out->clearAllModules();
     // Add the mobile js
     $out->addModules('ext.mobileFrontend2');
     // TODO: Hook for adding modules
     Profiler::instance()->setTemplated(true);
     $this->initPage($out);
     $tpl = $this->setupTemplate($this->template, 'skins');
     // Give the skin (us) to the template
     $tpl->setRef('skin', $this);
     // Language stuff
     $lang = $this->getLanguage();
     $userlang = $lang->getHtmlCode();
     $userdir = $lang->getDir();
     $tpl->set('lang', $userlang);
     $tpl->set('dir', $userdir);
     // Title
     $tpl->set('title', $out->getPageTitle());
     $tpl->set('pagetitle', $out->getHTMLTitle());
     // Scriptpath (Used for search and forms)
     $tpl->setRef('wgScript', $wgScript);
     // Mobile stuff
     $tpl->setRef('mobilelogopath', $wgMobileFrontend2Logo);
     # Add a <div class="mw-content-ltr/rtl"> around the body text
     # not for special pages or file pages AND only when viewing AND if the page exists
     # (or is in MW namespace, because that has default content)
     if (!in_array($title->getNamespace(), array(NS_SPECIAL, NS_FILE)) && in_array($request->getVal('action', 'view'), array('view', 'historysubmit')) && ($title->exists() || $title->getNamespace() == NS_MEDIAWIKI)) {
         $pageLang = $title->getPageLanguage();
         $realBodyAttribs = array('lang' => $pageLang->getHtmlCode(), 'dir' => $pageLang->getDir(), 'class' => 'mw-content-' . $pageLang->getDir());
         $out->mBodytext = Html::rawElement('div', $realBodyAttribs, $out->mBodytext);
     }
     $tpl->setRef('bodycontent', MobileFrontend2_PostParse::mangle($out->mBodytext));
     // CSS & JS
     // Make these last
     $tpl->set('headscripts', $this->getHeadScripts($out));
     $tpl->set('csslinks', $out->buildCssLinks());
     $tpl->set('bottomscripts', $this->bottomScripts());
     // Debug comments and stuff
     $tpl->set('debughtml', $this->generateDebugHTML());
     // Output
     $res = $tpl->execute();
     // result may be an error
     $this->printOrError($res);
 }
Exemplo n.º 16
0
 /**
  * The Symphony constructor initialises the class variables of Symphony. At present
  * constructor has a couple of responsibilities:
  * - Start a profiler instance
  * - If magic quotes are enabled, clean `$_SERVER`, `$_COOKIE`, `$_GET` and `$_POST` arrays 
  * - Initialise the correct Language for the currently logged in Author.
  * - Start the session and adjust the error handling if the user is logged in
  */
 protected function __construct()
 {
     self::$Profiler = Profiler::instance();
     if (get_magic_quotes_gpc()) {
         General::cleanArray($_SERVER);
         General::cleanArray($_COOKIE);
         General::cleanArray($_GET);
         General::cleanArray($_POST);
     }
     // Initialize language management
     Lang::initialize();
     Lang::set(self::$Configuration->get('lang', 'symphony'));
     self::initialiseCookie();
     // If the user is not a logged in Author, turn off the verbose error messages.
     if (!self::isLoggedIn() && is_null(self::$Author)) {
         GenericExceptionHandler::$enabled = false;
     }
     // Engine is ready.
     self::$Profiler->sample('Engine Initialisation');
 }
Exemplo n.º 17
0
 /**
  * Ends this task peacefully
  * @param string $mode Use 'fast' to always skip job running
  */
 public function restInPeace($mode = 'fast')
 {
     // Assure deferred updates are not in the main transaction
     wfGetLBFactory()->commitMasterChanges(__METHOD__);
     // Ignore things like master queries/connections on GET requests
     // as long as they are in deferred updates (which catch errors).
     Profiler::instance()->getTransactionProfiler()->resetExpectations();
     // Do any deferred jobs
     DeferredUpdates::doUpdates('enqueue');
     // Make sure any lazy jobs are pushed
     JobQueueGroup::pushLazyJobs();
     // Now that everything specific to this request is done,
     // try to occasionally run jobs (if enabled) from the queues
     if ($mode === 'normal') {
         $this->triggerJobs();
     }
     // Log profiling data, e.g. in the database or UDP
     wfLogProfilingData();
     // Commit and close up!
     $factory = wfGetLBFactory();
     $factory->commitMasterChanges(__METHOD__);
     $factory->shutdown(LBFactory::SHUTDOWN_NO_CHRONPROT);
     wfDebug("Request ended normally\n");
 }
Exemplo n.º 18
0
 /**
  * Return the text of a template, after recursively
  * replacing any variables or templates within the template.
  *
  * @param array $piece The parts of the template
  *  $piece['title']: the title, i.e. the part before the |
  *  $piece['parts']: the parameter array
  *  $piece['lineStart']: whether the brace was at the start of a line
  * @param PPFrame $frame The current frame, contains template arguments
  * @throws MWException
  * @return string The text of the template
  * @private
  */
 function braceSubstitution($piece, $frame)
 {
     wfProfileIn(__METHOD__);
     wfProfileIn(__METHOD__ . '-setup');
     # Flags
     $found = false;
     # $text has been filled
     $nowiki = false;
     # wiki markup in $text should be escaped
     $isHTML = false;
     # $text is HTML, armour it against wikitext transformation
     $forceRawInterwiki = false;
     # Force interwiki transclusion to be done in raw mode not rendered
     $isChildObj = false;
     # $text is a DOM node needing expansion in a child frame
     $isLocalObj = false;
     # $text is a DOM node needing expansion in the current frame
     # Title object, where $text came from
     $title = false;
     # $part1 is the bit before the first |, and must contain only title characters.
     # Various prefixes will be stripped from it later.
     $titleWithSpaces = $frame->expand($piece['title']);
     $part1 = trim($titleWithSpaces);
     $titleText = false;
     # Original title text preserved for various purposes
     $originalTitle = $part1;
     # $args is a list of argument nodes, starting from index 0, not including $part1
     # @todo FIXME: If piece['parts'] is null then the call to getLength() below won't work b/c this $args isn't an object
     $args = null == $piece['parts'] ? array() : $piece['parts'];
     wfProfileOut(__METHOD__ . '-setup');
     $titleProfileIn = null;
     // profile templates
     # SUBST
     wfProfileIn(__METHOD__ . '-modifiers');
     if (!$found) {
         $substMatch = $this->mSubstWords->matchStartAndRemove($part1);
         # Possibilities for substMatch: "subst", "safesubst" or FALSE
         # Decide whether to expand template or keep wikitext as-is.
         if ($this->ot['wiki']) {
             if ($substMatch === false) {
                 $literal = true;
                 # literal when in PST with no prefix
             } else {
                 $literal = false;
                 # expand when in PST with subst: or safesubst:
             }
         } else {
             if ($substMatch == 'subst') {
                 $literal = true;
                 # literal when not in PST with plain subst:
             } else {
                 $literal = false;
                 # expand when not in PST with safesubst: or no prefix
             }
         }
         if ($literal) {
             $text = $frame->virtualBracketedImplode('{{', '|', '}}', $titleWithSpaces, $args);
             $isLocalObj = true;
             $found = true;
         }
     }
     # Variables
     if (!$found && $args->getLength() == 0) {
         $id = $this->mVariables->matchStartToEnd($part1);
         if ($id !== false) {
             $text = $this->getVariableValue($id, $frame);
             if (MagicWord::getCacheTTL($id) > -1) {
                 $this->mOutput->updateCacheExpiry(MagicWord::getCacheTTL($id));
             }
             $found = true;
         }
     }
     # MSG, MSGNW and RAW
     if (!$found) {
         # Check for MSGNW:
         $mwMsgnw = MagicWord::get('msgnw');
         if ($mwMsgnw->matchStartAndRemove($part1)) {
             $nowiki = true;
         } else {
             # Remove obsolete MSG:
             $mwMsg = MagicWord::get('msg');
             $mwMsg->matchStartAndRemove($part1);
         }
         # Check for RAW:
         $mwRaw = MagicWord::get('raw');
         if ($mwRaw->matchStartAndRemove($part1)) {
             $forceRawInterwiki = true;
         }
     }
     wfProfileOut(__METHOD__ . '-modifiers');
     # Parser functions
     if (!$found) {
         wfProfileIn(__METHOD__ . '-pfunc');
         $colonPos = strpos($part1, ':');
         if ($colonPos !== false) {
             $func = substr($part1, 0, $colonPos);
             $funcArgs = array(trim(substr($part1, $colonPos + 1)));
             for ($i = 0; $i < $args->getLength(); $i++) {
                 $funcArgs[] = $args->item($i);
             }
             try {
                 $result = $this->callParserFunction($frame, $func, $funcArgs);
             } catch (Exception $ex) {
                 wfProfileOut(__METHOD__ . '-pfunc');
                 wfProfileOut(__METHOD__);
                 throw $ex;
             }
             # The interface for parser functions allows for extracting
             # flags into the local scope. Extract any forwarded flags
             # here.
             extract($result);
         }
         wfProfileOut(__METHOD__ . '-pfunc');
     }
     # Finish mangling title and then check for loops.
     # Set $title to a Title object and $titleText to the PDBK
     if (!$found) {
         $ns = NS_TEMPLATE;
         # Split the title into page and subpage
         $subpage = '';
         $relative = $this->maybeDoSubpageLink($part1, $subpage);
         if ($part1 !== $relative) {
             $part1 = $relative;
             $ns = $this->mTitle->getNamespace();
         }
         $title = Title::newFromText($part1, $ns);
         if ($title) {
             $titleText = $title->getPrefixedText();
             # Check for language variants if the template is not found
             if ($this->getConverterLanguage()->hasVariants() && $title->getArticleID() == 0) {
                 $this->getConverterLanguage()->findVariantLink($part1, $title, true);
             }
             # Do recursion depth check
             $limit = $this->mOptions->getMaxTemplateDepth();
             if ($frame->depth >= $limit) {
                 $found = true;
                 $text = '<span class="error">' . wfMessage('parser-template-recursion-depth-warning')->numParams($limit)->inContentLanguage()->text() . '</span>';
             }
         }
     }
     # Load from database
     if (!$found && $title) {
         if (!Profiler::instance()->isPersistent()) {
             # Too many unique items can kill profiling DBs/collectors
             $titleProfileIn = __METHOD__ . "-title-" . $title->getPrefixedDBkey();
             wfProfileIn($titleProfileIn);
             // template in
         }
         wfProfileIn(__METHOD__ . '-loadtpl');
         if (!$title->isExternal()) {
             if ($title->isSpecialPage() && $this->mOptions->getAllowSpecialInclusion() && $this->ot['html']) {
                 // Pass the template arguments as URL parameters.
                 // "uselang" will have no effect since the Language object
                 // is forced to the one defined in ParserOptions.
                 $pageArgs = array();
                 for ($i = 0; $i < $args->getLength(); $i++) {
                     $bits = $args->item($i)->splitArg();
                     if (strval($bits['index']) === '') {
                         $name = trim($frame->expand($bits['name'], PPFrame::STRIP_COMMENTS));
                         $value = trim($frame->expand($bits['value']));
                         $pageArgs[$name] = $value;
                     }
                 }
                 // Create a new context to execute the special page
                 $context = new RequestContext();
                 $context->setTitle($title);
                 $context->setRequest(new FauxRequest($pageArgs));
                 $context->setUser($this->getUser());
                 $context->setLanguage($this->mOptions->getUserLangObj());
                 $ret = SpecialPageFactory::capturePath($title, $context);
                 if ($ret) {
                     $text = $context->getOutput()->getHTML();
                     $this->mOutput->addOutputPageMetadata($context->getOutput());
                     $found = true;
                     $isHTML = true;
                     $this->disableCache();
                 }
             } elseif (MWNamespace::isNonincludable($title->getNamespace())) {
                 $found = false;
                 # access denied
                 wfDebug(__METHOD__ . ": template inclusion denied for " . $title->getPrefixedDBkey() . "\n");
             } else {
                 list($text, $title) = $this->getTemplateDom($title);
                 if ($text !== false) {
                     $found = true;
                     $isChildObj = true;
                 }
             }
             # If the title is valid but undisplayable, make a link to it
             if (!$found && ($this->ot['html'] || $this->ot['pre'])) {
                 $text = "[[:{$titleText}]]";
                 $found = true;
             }
         } elseif ($title->isTrans()) {
             # Interwiki transclusion
             if ($this->ot['html'] && !$forceRawInterwiki) {
                 $text = $this->interwikiTransclude($title, 'render');
                 $isHTML = true;
             } else {
                 $text = $this->interwikiTransclude($title, 'raw');
                 # Preprocess it like a template
                 $text = $this->preprocessToDom($text, self::PTD_FOR_INCLUSION);
                 $isChildObj = true;
             }
             $found = true;
         }
         # Do infinite loop check
         # This has to be done after redirect resolution to avoid infinite loops via redirects
         if (!$frame->loopCheck($title)) {
             $found = true;
             $text = '<span class="error">' . wfMessage('parser-template-loop-warning', $titleText)->inContentLanguage()->text() . '</span>';
             wfDebug(__METHOD__ . ": template loop broken at '{$titleText}'\n");
         }
         wfProfileOut(__METHOD__ . '-loadtpl');
     }
     # If we haven't found text to substitute by now, we're done
     # Recover the source wikitext and return it
     if (!$found) {
         $text = $frame->virtualBracketedImplode('{{', '|', '}}', $titleWithSpaces, $args);
         if ($titleProfileIn) {
             wfProfileOut($titleProfileIn);
             // template out
         }
         wfProfileOut(__METHOD__);
         return array('object' => $text);
     }
     # Expand DOM-style return values in a child frame
     if ($isChildObj) {
         # Clean up argument array
         $newFrame = $frame->newChild($args, $title);
         if ($nowiki) {
             $text = $newFrame->expand($text, PPFrame::RECOVER_ORIG);
         } elseif ($titleText !== false && $newFrame->isEmpty()) {
             # Expansion is eligible for the empty-frame cache
             if (isset($this->mTplExpandCache[$titleText])) {
                 $text = $this->mTplExpandCache[$titleText];
             } else {
                 $text = $newFrame->expand($text);
                 $this->mTplExpandCache[$titleText] = $text;
             }
         } else {
             # Uncached expansion
             $text = $newFrame->expand($text);
         }
     }
     if ($isLocalObj && $nowiki) {
         $text = $frame->expand($text, PPFrame::RECOVER_ORIG);
         $isLocalObj = false;
     }
     if ($titleProfileIn) {
         wfProfileOut($titleProfileIn);
         // template out
     }
     # Replace raw HTML by a placeholder
     if ($isHTML) {
         $text = $this->insertStripItem($text);
     } elseif ($nowiki && ($this->ot['html'] || $this->ot['pre'])) {
         # Escape nowiki-style return values
         $text = wfEscapeWikiText($text);
     } elseif (is_string($text) && !$piece['lineStart'] && preg_match('/^(?:{\\||:|;|#|\\*)/', $text)) {
         # Bug 529: if the template begins with a table or block-level
         # element, it should be treated as beginning a new line.
         # This behavior is somewhat controversial.
         $text = "\n" . $text;
     }
     if (is_string($text) && !$this->incrementIncludeSize('post-expand', strlen($text))) {
         # Error, oversize inclusion
         if ($titleText !== false) {
             # Make a working, properly escaped link if possible (bug 23588)
             $text = "[[:{$titleText}]]";
         } else {
             # This will probably not be a working link, but at least it may
             # provide some hint of where the problem is
             preg_replace('/^:/', '', $originalTitle);
             $text = "[[:{$originalTitle}]]";
         }
         $text .= $this->insertStripItem('<!-- WARNING: template omitted, post-expand include size too large -->');
         $this->limitationWarn('post-expand-template-inclusion');
     }
     if ($isLocalObj) {
         $ret = array('object' => $text);
     } else {
         $ret = array('text' => $text);
     }
     wfProfileOut(__METHOD__);
     return $ret;
 }
Exemplo n.º 19
0
 /**
  * Get a connection by index
  * This is the main entry point for this class.
  *
  * @param int $i Server index
  * @param array|string|bool $groups Query group(s), or false for the generic reader
  * @param string|bool $wiki Wiki ID, or false for the current wiki
  *
  * @throws MWException
  * @return DatabaseBase
  */
 public function getConnection($i, $groups = array(), $wiki = false)
 {
     if ($i === null || $i === false) {
         throw new MWException('Attempt to call ' . __METHOD__ . ' with invalid server index');
     }
     if ($wiki === wfWikiID()) {
         $wiki = false;
     }
     $groups = $groups === false || $groups === array() ? array(false) : (array) $groups;
     $masterOnly = $i == DB_MASTER || $i == $this->getWriterIndex();
     $oldConnsOpened = $this->connsOpened;
     // connections open now
     if ($i == DB_MASTER) {
         $i = $this->getWriterIndex();
     } else {
         # Try to find an available server in any the query groups (in order)
         foreach ($groups as $group) {
             $groupIndex = $this->getReaderIndex($group, $wiki);
             if ($groupIndex !== false) {
                 $i = $groupIndex;
                 break;
             }
         }
     }
     # Operation-based index
     if ($i == DB_SLAVE) {
         $this->mLastError = 'Unknown error';
         // reset error string
         # Try the general server pool if $groups are unavailable.
         $i = in_array(false, $groups, true) ? false : $this->getReaderIndex(false, $wiki);
         # Couldn't find a working server in getReaderIndex()?
         if ($i === false) {
             $this->mLastError = 'No working slave server: ' . $this->mLastError;
             return $this->reportConnectionError();
         }
     }
     # Now we have an explicit index into the servers array
     $conn = $this->openConnection($i, $wiki);
     if (!$conn) {
         return $this->reportConnectionError();
     }
     # Profile any new connections that happen
     if ($this->connsOpened > $oldConnsOpened) {
         $host = $conn->getServer();
         $dbname = $conn->getDBname();
         $trxProf = Profiler::instance()->getTransactionProfiler();
         $trxProf->recordConnection($host, $dbname, $masterOnly);
     }
     return $conn;
 }
Exemplo n.º 20
0
/**
 * Execute a shell command, with time and memory limits mirrored from the PHP
 * configuration if supported.
 *
 * @param string|string[] $cmd If string, a properly shell-escaped command line,
 *   or an array of unescaped arguments, in which case each value will be escaped
 *   Example:   [ 'convert', '-font', 'font name' ] would produce "'convert' '-font' 'font name'"
 * @param null|mixed &$retval Optional, will receive the program's exit code.
 *   (non-zero is usually failure). If there is an error from
 *   read, select, or proc_open(), this will be set to -1.
 * @param array $environ Optional environment variables which should be
 *   added to the executed command environment.
 * @param array $limits Optional array with limits(filesize, memory, time, walltime)
 *   this overwrites the global wgMaxShell* limits.
 * @param array $options Array of options:
 *   - duplicateStderr: Set this to true to duplicate stderr to stdout,
 *     including errors from limit.sh
 *   - profileMethod: By default this function will profile based on the calling
 *     method. Set this to a string for an alternative method to profile from
 *
 * @return string Collected stdout as a string
 */
function wfShellExec($cmd, &$retval = null, $environ = array(), $limits = array(), $options = array())
{
    global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime, $wgMaxShellWallClockTime, $wgShellCgroup;
    $disabled = wfShellExecDisabled();
    if ($disabled) {
        $retval = 1;
        return $disabled == 'safemode' ? 'Unable to run external programs in safe mode.' : 'Unable to run external programs, proc_open() is disabled.';
    }
    $includeStderr = isset($options['duplicateStderr']) && $options['duplicateStderr'];
    $profileMethod = isset($options['profileMethod']) ? $options['profileMethod'] : wfGetCaller();
    wfInitShellLocale();
    $envcmd = '';
    foreach ($environ as $k => $v) {
        if (wfIsWindows()) {
            /* Surrounding a set in quotes (method used by wfEscapeShellArg) makes the quotes themselves
             * appear in the environment variable, so we must use carat escaping as documented in
             * http://technet.microsoft.com/en-us/library/cc723564.aspx
             * Note however that the quote isn't listed there, but is needed, and the parentheses
             * are listed there but doesn't appear to need it.
             */
            $envcmd .= "set {$k}=" . preg_replace('/([&|()<>^"])/', '^\\1', $v) . '&& ';
        } else {
            /* Assume this is a POSIX shell, thus required to accept variable assignments before the command
             * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_09_01
             */
            $envcmd .= "{$k}=" . escapeshellarg($v) . ' ';
        }
    }
    if (is_array($cmd)) {
        $cmd = wfEscapeShellArg($cmd);
    }
    $cmd = $envcmd . $cmd;
    $useLogPipe = false;
    if (is_executable('/bin/bash')) {
        $time = intval(isset($limits['time']) ? $limits['time'] : $wgMaxShellTime);
        if (isset($limits['walltime'])) {
            $wallTime = intval($limits['walltime']);
        } elseif (isset($limits['time'])) {
            $wallTime = $time;
        } else {
            $wallTime = intval($wgMaxShellWallClockTime);
        }
        $mem = intval(isset($limits['memory']) ? $limits['memory'] : $wgMaxShellMemory);
        $filesize = intval(isset($limits['filesize']) ? $limits['filesize'] : $wgMaxShellFileSize);
        if ($time > 0 || $mem > 0 || $filesize > 0 || $wallTime > 0) {
            $cmd = '/bin/bash ' . escapeshellarg("{$IP}/includes/limit.sh") . ' ' . escapeshellarg($cmd) . ' ' . escapeshellarg("MW_INCLUDE_STDERR=" . ($includeStderr ? '1' : '') . ';' . "MW_CPU_LIMIT={$time}; " . 'MW_CGROUP=' . escapeshellarg($wgShellCgroup) . '; ' . "MW_MEM_LIMIT={$mem}; " . "MW_FILE_SIZE_LIMIT={$filesize}; " . "MW_WALL_CLOCK_LIMIT={$wallTime}; " . "MW_USE_LOG_PIPE=yes");
            $useLogPipe = true;
        } elseif ($includeStderr) {
            $cmd .= ' 2>&1';
        }
    } elseif ($includeStderr) {
        $cmd .= ' 2>&1';
    }
    wfDebug("wfShellExec: {$cmd}\n");
    $desc = array(0 => array('file', 'php://stdin', 'r'), 1 => array('pipe', 'w'), 2 => array('file', 'php://stderr', 'w'));
    if ($useLogPipe) {
        $desc[3] = array('pipe', 'w');
    }
    $pipes = null;
    $scoped = Profiler::instance()->scopedProfileIn(__FUNCTION__ . '-' . $profileMethod);
    $proc = proc_open($cmd, $desc, $pipes);
    if (!$proc) {
        wfDebugLog('exec', "proc_open() failed: {$cmd}");
        $retval = -1;
        return '';
    }
    $outBuffer = $logBuffer = '';
    $emptyArray = array();
    $status = false;
    $logMsg = false;
    // According to the documentation, it is possible for stream_select()
    // to fail due to EINTR. I haven't managed to induce this in testing
    // despite sending various signals. If it did happen, the error
    // message would take the form:
    //
    // stream_select(): unable to select [4]: Interrupted system call (max_fd=5)
    //
    // where [4] is the value of the macro EINTR and "Interrupted system
    // call" is string which according to the Linux manual is "possibly"
    // localised according to LC_MESSAGES.
    $eintr = defined('SOCKET_EINTR') ? SOCKET_EINTR : 4;
    $eintrMessage = "stream_select(): unable to select [{$eintr}]";
    // Build a table mapping resource IDs to pipe FDs to work around a
    // PHP 5.3 issue in which stream_select() does not preserve array keys
    // <https://bugs.php.net/bug.php?id=53427>.
    $fds = array();
    foreach ($pipes as $fd => $pipe) {
        $fds[(int) $pipe] = $fd;
    }
    $running = true;
    $timeout = null;
    $numReadyPipes = 0;
    while ($running === true || $numReadyPipes !== 0) {
        if ($running) {
            $status = proc_get_status($proc);
            // If the process has terminated, switch to nonblocking selects
            // for getting any data still waiting to be read.
            if (!$status['running']) {
                $running = false;
                $timeout = 0;
            }
        }
        $readyPipes = $pipes;
        // Clear last error
        // @codingStandardsIgnoreStart Generic.PHP.NoSilencedErrors.Discouraged
        @trigger_error('');
        $numReadyPipes = @stream_select($readyPipes, $emptyArray, $emptyArray, $timeout);
        if ($numReadyPipes === false) {
            // @codingStandardsIgnoreEnd
            $error = error_get_last();
            if (strncmp($error['message'], $eintrMessage, strlen($eintrMessage)) == 0) {
                continue;
            } else {
                trigger_error($error['message'], E_USER_WARNING);
                $logMsg = $error['message'];
                break;
            }
        }
        foreach ($readyPipes as $pipe) {
            $block = fread($pipe, 65536);
            $fd = $fds[(int) $pipe];
            if ($block === '') {
                // End of file
                fclose($pipes[$fd]);
                unset($pipes[$fd]);
                if (!$pipes) {
                    break 2;
                }
            } elseif ($block === false) {
                // Read error
                $logMsg = "Error reading from pipe";
                break 2;
            } elseif ($fd == 1) {
                // From stdout
                $outBuffer .= $block;
            } elseif ($fd == 3) {
                // From log FD
                $logBuffer .= $block;
                if (strpos($block, "\n") !== false) {
                    $lines = explode("\n", $logBuffer);
                    $logBuffer = array_pop($lines);
                    foreach ($lines as $line) {
                        wfDebugLog('exec', $line);
                    }
                }
            }
        }
    }
    foreach ($pipes as $pipe) {
        fclose($pipe);
    }
    // Use the status previously collected if possible, since proc_get_status()
    // just calls waitpid() which will not return anything useful the second time.
    if ($running) {
        $status = proc_get_status($proc);
    }
    if ($logMsg !== false) {
        // Read/select error
        $retval = -1;
        proc_close($proc);
    } elseif ($status['signaled']) {
        $logMsg = "Exited with signal {$status['termsig']}";
        $retval = 128 + $status['termsig'];
        proc_close($proc);
    } else {
        if ($status['running']) {
            $retval = proc_close($proc);
        } else {
            $retval = $status['exitcode'];
            proc_close($proc);
        }
        if ($retval == 127) {
            $logMsg = "Possibly missing executable file";
        } elseif ($retval >= 129 && $retval <= 192) {
            $logMsg = "Probably exited with signal " . ($retval - 128);
        }
    }
    if ($logMsg !== false) {
        wfDebugLog('exec', "{$logMsg}: {$cmd}");
    }
    return $outBuffer;
}
Exemplo n.º 21
0
        } else {
            $profName = $fname . '-extensions-' . implode('::', $func);
        }
    } else {
        $profName = $fname . '-extensions-' . strval($func);
    }
    $ps_ext_func = Profiler::instance()->scopedProfileIn($profName);
    call_user_func($func);
    Profiler::instance()->scopedProfileOut($ps_ext_func);
}
// If the session user has a 0 id but a valid name, that means we need to
// autocreate it.
if (!defined('MW_NO_SESSION') && !$wgCommandLineMode) {
    $sessionUser = MediaWiki\Session\SessionManager::getGlobalSession()->getUser();
    if ($sessionUser->getId() === 0 && User::isValidUserName($sessionUser->getName())) {
        $ps_autocreate = Profiler::instance()->scopedProfileIn($fname . '-autocreate');
        $res = MediaWiki\Auth\AuthManager::singleton()->autoCreateUser($sessionUser, MediaWiki\Auth\AuthManager::AUTOCREATE_SOURCE_SESSION, true);
        Profiler::instance()->scopedProfileOut($ps_autocreate);
        \MediaWiki\Logger\LoggerFactory::getInstance('authevents')->info('Autocreation attempt', ['event' => 'autocreate', 'status' => $res]);
        unset($res);
    }
    unset($sessionUser);
}
if (!$wgCommandLineMode) {
    Pingback::schedulePingback();
}
wfDebug("Fully initialised\n");
$wgFullyInitialised = true;
Profiler::instance()->scopedProfileOut($ps_extensions);
Profiler::instance()->scopedProfileOut($ps_setup);
Exemplo n.º 22
0
 function execute($sql)
 {
     if (defined("LIMB_APP_MODE") && LIMB_APP_MODE == "devel") {
         Profiler::instance()->addHit("Query");
         Profiler::instance()->startIncrementCheckpoint("sql_time");
     }
     $sql = mb_convert_encoding($sql, 'Windows-1251', 'UTF-8');
     $result = mssql_query($sql, $this->getConnectionId());
     if (defined("LIMB_APP_MODE") && LIMB_APP_MODE == "devel") {
         error_log($sql . "\n\n\n", 3, LIMB_VAR_DIR . '/log/query.log');
         Profiler::instance()->stopIncrementCheckpoint("sql_time");
     }
     if ($result === false) {
         $this->_raiseError($sql);
     }
     return $result;
 }
Exemplo n.º 23
0
 /**
  * initialize various variables and generate the template
  *
  * @param OutputPage $out
  */
 function outputPage(OutputPage $out = null)
 {
     Profiler::instance()->setTemplated(true);
     $oldContext = null;
     if ($out !== null) {
         // Deprecated since 1.20, note added in 1.25
         wfDeprecated(__METHOD__, '1.25');
         $oldContext = $this->getContext();
         $this->setContext($out->getContext());
     }
     $out = $this->getOutput();
     $this->initPage($out);
     $tpl = $this->prepareQuickTemplate($out);
     // execute template
     $res = $tpl->execute();
     // result may be an error
     $this->printOrError($res);
     if ($oldContext) {
         $this->setContext($oldContext);
     }
 }
Exemplo n.º 24
0
	/**
	 * Rollback a transaction previously started using begin().
	 * If no transaction is in progress, a warning is issued.
	 *
	 * No-op on non-transactional databases.
	 *
	 * @param $fname string
	 */
	final public function rollback( $fname = __METHOD__ ) {
		if ( !$this->mTrxLevel ) {
			wfWarn( "$fname: No transaction to rollback, something got out of sync!" );
		}
		$this->doRollback( $fname );
		$this->mTrxIdleCallbacks = array(); // cancel
		$this->mTrxPreCommitCallbacks = array(); // cancel
		if ( $this->mTrxDoneWrites ) {
			Profiler::instance()->transactionWritingOut( $this->mServer, $this->mDBname );
		}
		$this->mTrxDoneWrites = false;
	}
Exemplo n.º 25
0
 /**
  * Run jobs of the specified number/type for the specified time
  *
  * The response map has a 'job' field that lists status of each job, including:
  *   - type   : the job type
  *   - status : ok/failed
  *   - error  : any error message string
  *   - time   : the job run time in ms
  * The response map also has:
  *   - backoffs : the (job type => seconds) map of backoff times
  *   - elapsed  : the total time spent running tasks in ms
  *   - reached  : the reason the script finished, one of (none-ready, job-limit, time-limit)
  *
  * This method outputs status information only if a debug handler was set.
  * Any exceptions are caught and logged, but are not reported as output.
  *
  * @param array $options Map of parameters:
  *    - type     : the job type (or false for the default types)
  *    - maxJobs  : maximum number of jobs to run
  *    - maxTime  : maximum time in seconds before stopping
  *    - throttle : whether to respect job backoff configuration
  * @return array Summary response that can easily be JSON serialized
  */
 public function run(array $options)
 {
     global $wgJobClasses, $wgTrxProfilerLimits;
     $response = array('jobs' => array(), 'reached' => 'none-ready');
     $type = isset($options['type']) ? $options['type'] : false;
     $maxJobs = isset($options['maxJobs']) ? $options['maxJobs'] : false;
     $maxTime = isset($options['maxTime']) ? $options['maxTime'] : false;
     $noThrottle = isset($options['throttle']) && !$options['throttle'];
     if ($type !== false && !isset($wgJobClasses[$type])) {
         $response['reached'] = 'none-possible';
         return $response;
     }
     // Bail out if in read-only mode
     if (wfReadOnly()) {
         $response['reached'] = 'read-only';
         return $response;
     }
     // Catch huge single updates that lead to slave lag
     $trxProfiler = Profiler::instance()->getTransactionProfiler();
     $trxProfiler->setLogger(LoggerFactory::getInstance('DBPerformance'));
     $trxProfiler->setExpectations($wgTrxProfilerLimits['JobRunner'], __METHOD__);
     // Bail out if there is too much DB lag.
     // This check should not block as we want to try other wiki queues.
     $maxAllowedLag = 3;
     list(, $maxLag) = wfGetLB(wfWikiID())->getMaxLag();
     if ($maxLag >= $maxAllowedLag) {
         $response['reached'] = 'slave-lag-limit';
         return $response;
     }
     $group = JobQueueGroup::singleton();
     // Flush any pending DB writes for sanity
     wfGetLBFactory()->commitAll();
     // Some jobs types should not run until a certain timestamp
     $backoffs = array();
     // map of (type => UNIX expiry)
     $backoffDeltas = array();
     // map of (type => seconds)
     $wait = 'wait';
     // block to read backoffs the first time
     $stats = RequestContext::getMain()->getStats();
     $jobsPopped = 0;
     $timeMsTotal = 0;
     $flags = JobQueueGroup::USE_CACHE;
     $startTime = microtime(true);
     // time since jobs started running
     $checkLagPeriod = 1.0;
     // check slave lag this many seconds
     $lastCheckTime = 1;
     // timestamp of last slave check
     do {
         // Sync the persistent backoffs with concurrent runners
         $backoffs = $this->syncBackoffDeltas($backoffs, $backoffDeltas, $wait);
         $blacklist = $noThrottle ? array() : array_keys($backoffs);
         $wait = 'nowait';
         // less important now
         if ($type === false) {
             $job = $group->pop(JobQueueGroup::TYPE_DEFAULT, $flags, $blacklist);
         } elseif (in_array($type, $blacklist)) {
             $job = false;
             // requested queue in backoff state
         } else {
             $job = $group->pop($type);
             // job from a single queue
         }
         if ($job) {
             // found a job
             $popTime = time();
             $jType = $job->getType();
             // Back off of certain jobs for a while (for throttling and for errors)
             $ttw = $this->getBackoffTimeToWait($job);
             if ($ttw > 0) {
                 // Always add the delta for other runners in case the time running the
                 // job negated the backoff for each individually but not collectively.
                 $backoffDeltas[$jType] = isset($backoffDeltas[$jType]) ? $backoffDeltas[$jType] + $ttw : $ttw;
                 $backoffs = $this->syncBackoffDeltas($backoffs, $backoffDeltas, $wait);
             }
             $msg = $job->toString() . " STARTING";
             $this->logger->debug($msg);
             $this->debugCallback($msg);
             // Run the job...
             $jobStartTime = microtime(true);
             try {
                 ++$jobsPopped;
                 $status = $job->run();
                 $error = $job->getLastError();
                 $this->commitMasterChanges($job);
                 DeferredUpdates::doUpdates();
                 $this->commitMasterChanges($job);
             } catch (Exception $e) {
                 MWExceptionHandler::rollbackMasterChangesAndLog($e);
                 $status = false;
                 $error = get_class($e) . ': ' . $e->getMessage();
                 MWExceptionHandler::logException($e);
             }
             // Commit all outstanding connections that are in a transaction
             // to get a fresh repeatable read snapshot on every connection.
             // Note that jobs are still responsible for handling slave lag.
             wfGetLBFactory()->commitAll();
             // Clear out title cache data from prior snapshots
             LinkCache::singleton()->clear();
             $timeMs = intval((microtime(true) - $jobStartTime) * 1000);
             $timeMsTotal += $timeMs;
             // Record how long jobs wait before getting popped
             $readyTs = $job->getReadyTimestamp();
             if ($readyTs) {
                 $pickupDelay = $popTime - $readyTs;
                 $stats->timing('jobqueue.pickup_delay.all', 1000 * $pickupDelay);
                 $stats->timing("jobqueue.pickup_delay.{$jType}", 1000 * $pickupDelay);
             }
             // Record root job age for jobs being run
             $root = $job->getRootJobParams();
             if ($root['rootJobTimestamp']) {
                 $age = $popTime - wfTimestamp(TS_UNIX, $root['rootJobTimestamp']);
                 $stats->timing("jobqueue.pickup_root_age.{$jType}", 1000 * $age);
             }
             // Track the execution time for jobs
             $stats->timing("jobqueue.run.{$jType}", $timeMs);
             // Mark the job as done on success or when the job cannot be retried
             if ($status !== false || !$job->allowRetries()) {
                 $group->ack($job);
                 // done
             }
             // Back off of certain jobs for a while (for throttling and for errors)
             if ($status === false && mt_rand(0, 49) == 0) {
                 $ttw = max($ttw, 30);
                 // too many errors
                 $backoffDeltas[$jType] = isset($backoffDeltas[$jType]) ? $backoffDeltas[$jType] + $ttw : $ttw;
             }
             if ($status === false) {
                 $msg = $job->toString() . " t={$timeMs} error={$error}";
                 $this->logger->error($msg);
                 $this->debugCallback($msg);
             } else {
                 $msg = $job->toString() . " t={$timeMs} good";
                 $this->logger->info($msg);
                 $this->debugCallback($msg);
             }
             $response['jobs'][] = array('type' => $jType, 'status' => $status === false ? 'failed' : 'ok', 'error' => $error, 'time' => $timeMs);
             // Break out if we hit the job count or wall time limits...
             if ($maxJobs && $jobsPopped >= $maxJobs) {
                 $response['reached'] = 'job-limit';
                 break;
             } elseif ($maxTime && microtime(true) - $startTime > $maxTime) {
                 $response['reached'] = 'time-limit';
                 break;
             }
             // Don't let any of the main DB slaves get backed up.
             // This only waits for so long before exiting and letting
             // other wikis in the farm (on different masters) get a chance.
             $timePassed = microtime(true) - $lastCheckTime;
             if ($timePassed >= $checkLagPeriod || $timePassed < 0) {
                 if (!wfWaitForSlaves($lastCheckTime, false, '*', $maxAllowedLag)) {
                     $response['reached'] = 'slave-lag-limit';
                     break;
                 }
                 $lastCheckTime = microtime(true);
             }
             // Don't let any queue slaves/backups fall behind
             if ($jobsPopped > 0 && $jobsPopped % 100 == 0) {
                 $group->waitForBackups();
             }
             // Bail if near-OOM instead of in a job
             if (!$this->checkMemoryOK()) {
                 $response['reached'] = 'memory-limit';
                 break;
             }
         }
     } while ($job);
     // stop when there are no jobs
     // Sync the persistent backoffs for the next runJobs.php pass
     if ($backoffDeltas) {
         $this->syncBackoffDeltas($backoffs, $backoffDeltas, 'wait');
     }
     $response['backoffs'] = $backoffs;
     $response['elapsed'] = $timeMsTotal;
     return $response;
 }
Exemplo n.º 26
0
 /**
  * Get a Swift container stat array, possibly from process cache.
  * Use $reCache if the file count or byte count is needed.
  *
  * @param string $container Container name
  * @param bool $bypassCache Bypass all caches and load from Swift
  * @return array|bool|null False on 404, null on failure
  */
 protected function getContainerStat($container, $bypassCache = false)
 {
     $ps = Profiler::instance()->scopedProfileIn(__METHOD__ . "-{$this->name}");
     if ($bypassCache) {
         // purge cache
         $this->containerStatCache->clear($container);
     } elseif (!$this->containerStatCache->has($container, 'stat')) {
         $this->primeContainerCache(array($container));
         // check persistent cache
     }
     if (!$this->containerStatCache->has($container, 'stat')) {
         $auth = $this->getAuthentication();
         if (!$auth) {
             return null;
         }
         list($rcode, $rdesc, $rhdrs, $rbody, $rerr) = $this->http->run(array('method' => 'HEAD', 'url' => $this->storageUrl($auth, $container), 'headers' => $this->authTokenHeaders($auth)));
         if ($rcode === 204) {
             $stat = array('count' => $rhdrs['x-container-object-count'], 'bytes' => $rhdrs['x-container-bytes-used']);
             if ($bypassCache) {
                 return $stat;
             } else {
                 $this->containerStatCache->set($container, 'stat', $stat);
                 // cache it
                 $this->setContainerCache($container, $stat);
                 // update persistent cache
             }
         } elseif ($rcode === 404) {
             return false;
         } else {
             $this->onError(null, __METHOD__, array('cont' => $container), $rerr, $rcode, $rdesc);
             return null;
         }
     }
     return $this->containerStatCache->get($container, 'stat');
 }
Exemplo n.º 27
0
 /**
  * Execute a special page path.
  * The path may contain parameters, e.g. Special:Name/Params
  * Extracts the special page name and call the execute method, passing the parameters
  *
  * Returns a title object if the page is redirected, false if there was no such special
  * page, and true if it was successful.
  *
  * @param Title $title
  * @param IContextSource $context
  * @param bool $including Bool output is being captured for use in {{special:whatever}}
  *
  * @return bool
  */
 public static function executePath(Title &$title, IContextSource &$context, $including = false)
 {
     // @todo FIXME: Redirects broken due to this call
     $bits = explode('/', $title->getDBkey(), 2);
     $name = $bits[0];
     if (!isset($bits[1])) {
         // bug 2087
         $par = null;
     } else {
         $par = $bits[1];
     }
     $page = self::getPage($name);
     if (!$page) {
         $context->getOutput()->setArticleRelated(false);
         $context->getOutput()->setRobotPolicy('noindex,nofollow');
         global $wgSend404Code;
         if ($wgSend404Code) {
             $context->getOutput()->setStatusCode(404);
         }
         $context->getOutput()->showErrorPage('nosuchspecialpage', 'nospecialpagetext');
         return false;
     }
     if (!$including) {
         // Narrow DB query expectations for this HTTP request
         $trxLimits = $context->getConfig()->get('TrxProfilerLimits');
         $trxProfiler = Profiler::instance()->getTransactionProfiler();
         if ($context->getRequest()->wasPosted() && !$page->doesWrites()) {
             $trxProfiler->setExpectations($trxLimits['POST-nonwrite'], __METHOD__);
             $context->getRequest()->markAsSafeRequest();
         }
     }
     // Page exists, set the context
     $page->setContext($context);
     if (!$including) {
         // Redirect to canonical alias for GET commands
         // Not for POST, we'd lose the post data, so it's best to just distribute
         // the request. Such POST requests are possible for old extensions that
         // generate self-links without being aware that their default name has
         // changed.
         if ($name != $page->getLocalName() && !$context->getRequest()->wasPosted()) {
             $query = $context->getRequest()->getQueryValues();
             unset($query['title']);
             $title = $page->getPageTitle($par);
             $url = $title->getFullURL($query);
             $context->getOutput()->redirect($url);
             return $title;
         } else {
             $context->setTitle($page->getPageTitle($par));
         }
     } elseif (!$page->isIncludable()) {
         return false;
     }
     $page->including($including);
     // Execute special page
     $page->run($par);
     return true;
 }
Exemplo n.º 28
0
 /**
  * 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;
 }
Exemplo n.º 29
0
 /**
  * Auto-create the given user, if necessary
  * @private Don't call this yourself. Let Setup.php do it for you at the right time.
  * @note This more properly belongs in AuthManager, but we need it now.
  *  When AuthManager comes, this will be deprecated and will pass-through
  *  to the corresponding AuthManager method.
  * @param User $user User to auto-create
  * @return bool Success
  */
 public static function autoCreateUser(User $user)
 {
     global $wgAuth;
     $logger = self::singleton()->logger;
     // Much of this code is based on that in CentralAuth
     // Try the local user from the slave DB
     $localId = User::idFromName($user->getName());
     // Fetch the user ID from the master, so that we don't try to create the user
     // when they already exist, due to replication lag
     // @codeCoverageIgnoreStart
     if (!$localId && wfGetLB()->getReaderIndex() != 0) {
         $localId = User::idFromName($user->getName(), User::READ_LATEST);
     }
     // @codeCoverageIgnoreEnd
     if ($localId) {
         // User exists after all.
         $user->setId($localId);
         $user->loadFromId();
         return false;
     }
     // Denied by AuthPlugin? But ignore AuthPlugin itself.
     if (get_class($wgAuth) !== 'AuthPlugin' && !$wgAuth->autoCreate()) {
         $logger->debug(__METHOD__ . ': denied by AuthPlugin');
         $user->setId(0);
         $user->loadFromId();
         return false;
     }
     // Wiki is read-only?
     if (wfReadOnly()) {
         $logger->debug(__METHOD__ . ': denied by wfReadOnly()');
         $user->setId(0);
         $user->loadFromId();
         return false;
     }
     $userName = $user->getName();
     // Check the session, if we tried to create this user already there's
     // no point in retrying.
     $session = self::getGlobalSession();
     $reason = $session->get('MWSession::AutoCreateBlacklist');
     if ($reason) {
         $logger->debug(__METHOD__ . ": blacklisted in session ({$reason})");
         $user->setId(0);
         $user->loadFromId();
         return false;
     }
     // Is the IP user able to create accounts?
     $anon = new User();
     if (!$anon->isAllowedAny('createaccount', 'autocreateaccount') || $anon->isBlockedFromCreateAccount()) {
         // Blacklist the user to avoid repeated DB queries subsequently
         $logger->debug(__METHOD__ . ': user is blocked from this wiki, blacklisting');
         $session->set('MWSession::AutoCreateBlacklist', 'blocked', 600);
         $session->persist();
         $user->setId(0);
         $user->loadFromId();
         return false;
     }
     // Check for validity of username
     if (!User::isCreatableName($userName)) {
         $logger->debug(__METHOD__ . ': Invalid username, blacklisting');
         $session->set('MWSession::AutoCreateBlacklist', 'invalid username', 600);
         $session->persist();
         $user->setId(0);
         $user->loadFromId();
         return false;
     }
     // Give other extensions a chance to stop auto creation.
     $user->loadDefaults($userName);
     $abortMessage = '';
     if (!\Hooks::run('AbortAutoAccount', array($user, &$abortMessage))) {
         // In this case we have no way to return the message to the user,
         // but we can log it.
         $logger->debug(__METHOD__ . ": denied by hook: {$abortMessage}");
         $session->set('MWSession::AutoCreateBlacklist', "hook aborted: {$abortMessage}", 600);
         $session->persist();
         $user->setId(0);
         $user->loadFromId();
         return false;
     }
     // Make sure the name has not been changed
     if ($user->getName() !== $userName) {
         $user->setId(0);
         $user->loadFromId();
         throw new \UnexpectedValueException('AbortAutoAccount hook tried to change the user name');
     }
     // Ignore warnings about master connections/writes...hard to avoid here
     \Profiler::instance()->getTransactionProfiler()->resetExpectations();
     $cache = \ObjectCache::getLocalClusterInstance();
     $backoffKey = wfMemcKey('MWSession', 'autocreate-failed', md5($userName));
     if ($cache->get($backoffKey)) {
         $logger->debug(__METHOD__ . ': denied by prior creation attempt failures');
         $user->setId(0);
         $user->loadFromId();
         return false;
     }
     // Checks passed, create the user...
     $from = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : 'CLI';
     $logger->info(__METHOD__ . ": creating new user ({$userName}) - from: {$from}");
     try {
         // Insert the user into the local DB master
         $status = $user->addToDatabase();
         if (!$status->isOK()) {
             // @codeCoverageIgnoreStart
             $logger->error(__METHOD__ . ': failed with message ' . $status->getWikiText());
             $user->setId(0);
             $user->loadFromId();
             return false;
             // @codeCoverageIgnoreEnd
         }
     } catch (\Exception $ex) {
         // @codeCoverageIgnoreStart
         $logger->error(__METHOD__ . ': failed with exception ' . $ex->getMessage());
         // Do not keep throwing errors for a while
         $cache->set($backoffKey, 1, 600);
         // Bubble up error; which should normally trigger DB rollbacks
         throw $ex;
         // @codeCoverageIgnoreEnd
     }
     # Notify hooks (e.g. Newuserlog)
     \Hooks::run('AuthPluginAutoCreate', array($user));
     \Hooks::run('LocalUserCreated', array($user, true));
     # Notify AuthPlugin too
     $tmpUser = $user;
     $wgAuth->initUser($tmpUser, true);
     if ($tmpUser !== $user) {
         $logger->warning(__METHOD__ . ': ' . get_class($wgAuth) . '::initUser() replaced the user object');
     }
     $user->saveSettings();
     # Update user count
     \DeferredUpdates::addUpdate(new \SiteStatsUpdate(0, 0, 0, 0, 1));
     # Watch user's userpage and talk page
     $user->addWatch($user->getUserPage(), \WatchedItem::IGNORE_USER_RIGHTS);
     return true;
 }
Exemplo n.º 30
0
 /**
  * Ends this task peacefully
  * @param string $mode Use 'fast' to always skip job running
  */
 public function restInPeace($mode = 'fast')
 {
     $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
     // Assure deferred updates are not in the main transaction
     $lbFactory->commitMasterChanges(__METHOD__);
     // Loosen DB query expectations since the HTTP client is unblocked
     $trxProfiler = Profiler::instance()->getTransactionProfiler();
     $trxProfiler->resetExpectations();
     $trxProfiler->setExpectations($this->config->get('TrxProfilerLimits')['PostSend'], __METHOD__);
     // Do any deferred jobs
     DeferredUpdates::doUpdates('enqueue');
     DeferredUpdates::setImmediateMode(true);
     // Make sure any lazy jobs are pushed
     JobQueueGroup::pushLazyJobs();
     // Now that everything specific to this request is done,
     // try to occasionally run jobs (if enabled) from the queues
     if ($mode === 'normal') {
         $this->triggerJobs();
     }
     // Log profiling data, e.g. in the database or UDP
     wfLogProfilingData();
     // Commit and close up!
     $lbFactory->commitMasterChanges(__METHOD__);
     $lbFactory->shutdown(LBFactory::SHUTDOWN_NO_CHRONPROT);
     wfDebug("Request ended normally\n");
 }