[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/ -> MediaWiki.php (source)

   1  <?php
   2  /**
   3   * Helper class for the index.php entry point.
   4   *
   5   * This program is free software; you can redistribute it and/or modify
   6   * it under the terms of the GNU General Public License as published by
   7   * the Free Software Foundation; either version 2 of the License, or
   8   * (at your option) any later version.
   9   *
  10   * This program is distributed in the hope that it will be useful,
  11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13   * GNU General Public License for more details.
  14   *
  15   * You should have received a copy of the GNU General Public License along
  16   * with this program; if not, write to the Free Software Foundation, Inc.,
  17   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18   * http://www.gnu.org/copyleft/gpl.html
  19   *
  20   * @file
  21   */
  22  
  23  /**
  24   * The MediaWiki class is the helper class for the index.php entry point.
  25   *
  26   * @internal documentation reviewed 15 Mar 2010
  27   */
  28  class MediaWiki {
  29      /**
  30       * @var IContextSource
  31       */
  32      private $context;
  33  
  34      /**
  35       * @var Config
  36       */
  37      private $config;
  38  
  39      /**
  40       * @param IContextSource|null $context
  41       */
  42  	public function __construct( IContextSource $context = null ) {
  43          if ( !$context ) {
  44              $context = RequestContext::getMain();
  45          }
  46  
  47          $this->context = $context;
  48          $this->config = $context->getConfig();
  49      }
  50  
  51      /**
  52       * Parse the request to get the Title object
  53       *
  54       * @return Title Title object to be $wgTitle
  55       */
  56  	private function parseTitle() {
  57          global $wgContLang;
  58  
  59          $request = $this->context->getRequest();
  60          $curid = $request->getInt( 'curid' );
  61          $title = $request->getVal( 'title' );
  62          $action = $request->getVal( 'action', 'view' );
  63  
  64          if ( $request->getCheck( 'search' ) ) {
  65              // Compatibility with old search URLs which didn't use Special:Search
  66              // Just check for presence here, so blank requests still
  67              // show the search page when using ugly URLs (bug 8054).
  68              $ret = SpecialPage::getTitleFor( 'Search' );
  69          } elseif ( $curid ) {
  70              // URLs like this are generated by RC, because rc_title isn't always accurate
  71              $ret = Title::newFromID( $curid );
  72          } else {
  73              $ret = Title::newFromURL( $title );
  74              // Alias NS_MEDIA page URLs to NS_FILE...we only use NS_MEDIA
  75              // in wikitext links to tell Parser to make a direct file link
  76              if ( !is_null( $ret ) && $ret->getNamespace() == NS_MEDIA ) {
  77                  $ret = Title::makeTitle( NS_FILE, $ret->getDBkey() );
  78              }
  79              // Check variant links so that interwiki links don't have to worry
  80              // about the possible different language variants
  81              if ( count( $wgContLang->getVariants() ) > 1
  82                  && !is_null( $ret ) && $ret->getArticleID() == 0
  83              ) {
  84                  $wgContLang->findVariantLink( $title, $ret );
  85              }
  86          }
  87  
  88          // If title is not provided, always allow oldid and diff to set the title.
  89          // If title is provided, allow oldid and diff to override the title, unless
  90          // we are talking about a special page which might use these parameters for
  91          // other purposes.
  92          if ( $ret === null || !$ret->isSpecialPage() ) {
  93              // We can have urls with just ?diff=,?oldid= or even just ?diff=
  94              $oldid = $request->getInt( 'oldid' );
  95              $oldid = $oldid ? $oldid : $request->getInt( 'diff' );
  96              // Allow oldid to override a changed or missing title
  97              if ( $oldid ) {
  98                  $rev = Revision::newFromId( $oldid );
  99                  $ret = $rev ? $rev->getTitle() : $ret;
 100              }
 101          }
 102  
 103          // Use the main page as default title if nothing else has been provided
 104          if ( $ret === null
 105              && strval( $title ) === ''
 106              && !$request->getCheck( 'curid' )
 107              && $action !== 'delete'
 108          ) {
 109              $ret = Title::newMainPage();
 110          }
 111  
 112          if ( $ret === null || ( $ret->getDBkey() == '' && !$ret->isExternal() ) ) {
 113              $ret = SpecialPage::getTitleFor( 'Badtitle' );
 114          }
 115  
 116          return $ret;
 117      }
 118  
 119      /**
 120       * Get the Title object that we'll be acting on, as specified in the WebRequest
 121       * @return Title
 122       */
 123  	public function getTitle() {
 124          if ( $this->context->getTitle() === null ) {
 125              $this->context->setTitle( $this->parseTitle() );
 126          }
 127          return $this->context->getTitle();
 128      }
 129  
 130      /**
 131       * Returns the name of the action that will be executed.
 132       *
 133       * @return string Action
 134       */
 135  	public function getAction() {
 136          static $action = null;
 137  
 138          if ( $action === null ) {
 139              $action = Action::getActionName( $this->context );
 140          }
 141  
 142          return $action;
 143      }
 144  
 145      /**
 146       * Performs the request.
 147       * - bad titles
 148       * - read restriction
 149       * - local interwiki redirects
 150       * - redirect loop
 151       * - special pages
 152       * - normal pages
 153       *
 154       * @throws MWException|PermissionsError|BadTitleError|HttpError
 155       * @return void
 156       */
 157  	private function performRequest() {
 158          global $wgTitle;
 159  
 160          wfProfileIn( __METHOD__ );
 161  
 162          $request = $this->context->getRequest();
 163          $requestTitle = $title = $this->context->getTitle();
 164          $output = $this->context->getOutput();
 165          $user = $this->context->getUser();
 166  
 167          if ( $request->getVal( 'printable' ) === 'yes' ) {
 168              $output->setPrintable();
 169          }
 170  
 171          $unused = null; // To pass it by reference
 172          wfRunHooks( 'BeforeInitialize', array( &$title, &$unused, &$output, &$user, $request, $this ) );
 173  
 174          // Invalid titles. Bug 21776: The interwikis must redirect even if the page name is empty.
 175          if ( is_null( $title ) || ( $title->getDBkey() == '' && !$title->isExternal() )
 176              || $title->isSpecial( 'Badtitle' )
 177          ) {
 178              $this->context->setTitle( SpecialPage::getTitleFor( 'Badtitle' ) );
 179              wfProfileOut( __METHOD__ );
 180              throw new BadTitleError();
 181          }
 182  
 183          // Check user's permissions to read this page.
 184          // We have to check here to catch special pages etc.
 185          // We will check again in Article::view().
 186          $permErrors = $title->isSpecial( 'RunJobs' )
 187              ? array() // relies on HMAC key signature alone
 188              : $title->getUserPermissionsErrors( 'read', $user );
 189          if ( count( $permErrors ) ) {
 190              // Bug 32276: allowing the skin to generate output with $wgTitle or
 191              // $this->context->title set to the input title would allow anonymous users to
 192              // determine whether a page exists, potentially leaking private data. In fact, the
 193              // curid and oldid request  parameters would allow page titles to be enumerated even
 194              // when they are not guessable. So we reset the title to Special:Badtitle before the
 195              // permissions error is displayed.
 196              //
 197              // The skin mostly uses $this->context->getTitle() these days, but some extensions
 198              // still use $wgTitle.
 199  
 200              $badTitle = SpecialPage::getTitleFor( 'Badtitle' );
 201              $this->context->setTitle( $badTitle );
 202              $wgTitle = $badTitle;
 203  
 204              wfProfileOut( __METHOD__ );
 205              throw new PermissionsError( 'read', $permErrors );
 206          }
 207  
 208          $pageView = false; // was an article or special page viewed?
 209  
 210          // Interwiki redirects
 211          if ( $title->isExternal() ) {
 212              $rdfrom = $request->getVal( 'rdfrom' );
 213              if ( $rdfrom ) {
 214                  $url = $title->getFullURL( array( 'rdfrom' => $rdfrom ) );
 215              } else {
 216                  $query = $request->getValues();
 217                  unset( $query['title'] );
 218                  $url = $title->getFullURL( $query );
 219              }
 220              // Check for a redirect loop
 221              if ( !preg_match( '/^' . preg_quote( $this->config->get( 'Server' ), '/' ) . '/', $url )
 222                  && $title->isLocal()
 223              ) {
 224                  // 301 so google et al report the target as the actual url.
 225                  $output->redirect( $url, 301 );
 226              } else {
 227                  $this->context->setTitle( SpecialPage::getTitleFor( 'Badtitle' ) );
 228                  wfProfileOut( __METHOD__ );
 229                  throw new BadTitleError();
 230              }
 231          // Redirect loops, no title in URL, $wgUsePathInfo URLs, and URLs with a variant
 232          } elseif ( $request->getVal( 'action', 'view' ) == 'view' && !$request->wasPosted()
 233              && ( $request->getVal( 'title' ) === null
 234                  || $title->getPrefixedDBkey() != $request->getVal( 'title' ) )
 235              && !count( $request->getValueNames( array( 'action', 'title' ) ) )
 236              && wfRunHooks( 'TestCanonicalRedirect', array( $request, $title, $output ) )
 237          ) {
 238              if ( $title->isSpecialPage() ) {
 239                  list( $name, $subpage ) = SpecialPageFactory::resolveAlias( $title->getDBkey() );
 240                  if ( $name ) {
 241                      $title = SpecialPage::getTitleFor( $name, $subpage );
 242                  }
 243              }
 244              $targetUrl = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
 245              // Redirect to canonical url, make it a 301 to allow caching
 246              if ( $targetUrl == $request->getFullRequestURL() ) {
 247                  $message = "Redirect loop detected!\n\n" .
 248                      "This means the wiki got confused about what page was " .
 249                      "requested; this sometimes happens when moving a wiki " .
 250                      "to a new server or changing the server configuration.\n\n";
 251  
 252                  if ( $this->config->get( 'UsePathInfo' ) ) {
 253                      $message .= "The wiki is trying to interpret the page " .
 254                          "title from the URL path portion (PATH_INFO), which " .
 255                          "sometimes fails depending on the web server. Try " .
 256                          "setting \"\$wgUsePathInfo = false;\" in your " .
 257                          "LocalSettings.php, or check that \$wgArticlePath " .
 258                          "is correct.";
 259                  } else {
 260                      $message .= "Your web server was detected as possibly not " .
 261                          "supporting URL path components (PATH_INFO) correctly; " .
 262                          "check your LocalSettings.php for a customized " .
 263                          "\$wgArticlePath setting and/or toggle \$wgUsePathInfo " .
 264                          "to true.";
 265                  }
 266                  throw new HttpError( 500, $message );
 267              } else {
 268                  $output->setSquidMaxage( 1200 );
 269                  $output->redirect( $targetUrl, '301' );
 270              }
 271          // Special pages
 272          } elseif ( NS_SPECIAL == $title->getNamespace() ) {
 273              $pageView = true;
 274              // Actions that need to be made when we have a special pages
 275              SpecialPageFactory::executePath( $title, $this->context );
 276          } else {
 277              // ...otherwise treat it as an article view. The article
 278              // may be a redirect to another article or URL.
 279              $article = $this->initializeArticle();
 280              if ( is_object( $article ) ) {
 281                  $pageView = true;
 282                  $this->performAction( $article, $requestTitle );
 283              } elseif ( is_string( $article ) ) {
 284                  $output->redirect( $article );
 285              } else {
 286                  wfProfileOut( __METHOD__ );
 287                  throw new MWException( "Shouldn't happen: MediaWiki::initializeArticle()"
 288                      . " returned neither an object nor a URL" );
 289              }
 290          }
 291  
 292          if ( $pageView ) {
 293              // Promote user to any groups they meet the criteria for
 294              $user->addAutopromoteOnceGroups( 'onView' );
 295          }
 296  
 297          wfProfileOut( __METHOD__ );
 298      }
 299  
 300      /**
 301       * Initialize the main Article object for "standard" actions (view, etc)
 302       * Create an Article object for the page, following redirects if needed.
 303       *
 304       * @return mixed An Article, or a string to redirect to another URL
 305       */
 306  	private function initializeArticle() {
 307          wfProfileIn( __METHOD__ );
 308  
 309          $title = $this->context->getTitle();
 310          if ( $this->context->canUseWikiPage() ) {
 311              // Try to use request context wiki page, as there
 312              // is already data from db saved in per process
 313              // cache there from this->getAction() call.
 314              $page = $this->context->getWikiPage();
 315              $article = Article::newFromWikiPage( $page, $this->context );
 316          } else {
 317              // This case should not happen, but just in case.
 318              $article = Article::newFromTitle( $title, $this->context );
 319              $this->context->setWikiPage( $article->getPage() );
 320          }
 321  
 322          // NS_MEDIAWIKI has no redirects.
 323          // It is also used for CSS/JS, so performance matters here...
 324          if ( $title->getNamespace() == NS_MEDIAWIKI ) {
 325              wfProfileOut( __METHOD__ );
 326              return $article;
 327          }
 328  
 329          $request = $this->context->getRequest();
 330  
 331          // Namespace might change when using redirects
 332          // Check for redirects ...
 333          $action = $request->getVal( 'action', 'view' );
 334          $file = ( $title->getNamespace() == NS_FILE ) ? $article->getFile() : null;
 335          if ( ( $action == 'view' || $action == 'render' ) // ... for actions that show content
 336              && !$request->getVal( 'oldid' ) // ... and are not old revisions
 337              && !$request->getVal( 'diff' ) // ... and not when showing diff
 338              && $request->getVal( 'redirect' ) != 'no' // ... unless explicitly told not to
 339              // ... and the article is not a non-redirect image page with associated file
 340              && !( is_object( $file ) && $file->exists() && !$file->getRedirected() )
 341          ) {
 342              // Give extensions a change to ignore/handle redirects as needed
 343              $ignoreRedirect = $target = false;
 344  
 345              wfRunHooks( 'InitializeArticleMaybeRedirect',
 346                  array( &$title, &$request, &$ignoreRedirect, &$target, &$article ) );
 347  
 348              // Follow redirects only for... redirects.
 349              // If $target is set, then a hook wanted to redirect.
 350              if ( !$ignoreRedirect && ( $target || $article->isRedirect() ) ) {
 351                  // Is the target already set by an extension?
 352                  $target = $target ? $target : $article->followRedirect();
 353                  if ( is_string( $target ) ) {
 354                      if ( !$this->config->get( 'DisableHardRedirects' ) ) {
 355                          // we'll need to redirect
 356                          wfProfileOut( __METHOD__ );
 357                          return $target;
 358                      }
 359                  }
 360                  if ( is_object( $target ) ) {
 361                      // Rewrite environment to redirected article
 362                      $rarticle = Article::newFromTitle( $target, $this->context );
 363                      $rarticle->loadPageData();
 364                      if ( $rarticle->exists() || ( is_object( $file ) && !$file->isLocal() ) ) {
 365                          $rarticle->setRedirectedFrom( $title );
 366                          $article = $rarticle;
 367                          $this->context->setTitle( $target );
 368                          $this->context->setWikiPage( $article->getPage() );
 369                      }
 370                  }
 371              } else {
 372                  $this->context->setTitle( $article->getTitle() );
 373                  $this->context->setWikiPage( $article->getPage() );
 374              }
 375          }
 376  
 377          wfProfileOut( __METHOD__ );
 378          return $article;
 379      }
 380  
 381      /**
 382       * Perform one of the "standard" actions
 383       *
 384       * @param Page $page
 385       * @param Title $requestTitle The original title, before any redirects were applied
 386       */
 387  	private function performAction( Page $page, Title $requestTitle ) {
 388          wfProfileIn( __METHOD__ );
 389  
 390          $request = $this->context->getRequest();
 391          $output = $this->context->getOutput();
 392          $title = $this->context->getTitle();
 393          $user = $this->context->getUser();
 394  
 395          if ( !wfRunHooks( 'MediaWikiPerformAction',
 396                  array( $output, $page, $title, $user, $request, $this ) )
 397          ) {
 398              wfProfileOut( __METHOD__ );
 399              return;
 400          }
 401  
 402          $act = $this->getAction();
 403  
 404          $action = Action::factory( $act, $page, $this->context );
 405  
 406          if ( $action instanceof Action ) {
 407              # Let Squid cache things if we can purge them.
 408              if ( $this->config->get( 'UseSquid' ) &&
 409                  in_array( $request->getFullRequestURL(), $requestTitle->getSquidURLs() )
 410              ) {
 411                  $output->setSquidMaxage( $this->config->get( 'SquidMaxage' ) );
 412              }
 413  
 414              $action->show();
 415              wfProfileOut( __METHOD__ );
 416              return;
 417          }
 418  
 419          if ( wfRunHooks( 'UnknownAction', array( $request->getVal( 'action', 'view' ), $page ) ) ) {
 420              $output->setStatusCode( 404 );
 421              $output->showErrorPage( 'nosuchaction', 'nosuchactiontext' );
 422          }
 423  
 424          wfProfileOut( __METHOD__ );
 425      }
 426  
 427      /**
 428       * Run the current MediaWiki instance
 429       * index.php just calls this
 430       */
 431  	public function run() {
 432          try {
 433              $this->checkMaxLag();
 434              try {
 435                  $this->main();
 436              } catch ( ErrorPageError $e ) {
 437                  // Bug 62091: while exceptions are convenient to bubble up GUI errors,
 438                  // they are not internal application faults. As with normal requests, this
 439                  // should commit, print the output, do deferred updates, jobs, and profiling.
 440                  wfGetLBFactory()->commitMasterChanges();
 441                  $e->report(); // display the GUI error
 442              }
 443              if ( function_exists( 'fastcgi_finish_request' ) ) {
 444                  fastcgi_finish_request();
 445              }
 446              $this->triggerJobs();
 447              $this->restInPeace();
 448          } catch ( Exception $e ) {
 449              MWExceptionHandler::handle( $e );
 450          }
 451      }
 452  
 453      /**
 454       * Checks if the request should abort due to a lagged server,
 455       * for given maxlag parameter.
 456       * @return bool
 457       */
 458  	private function checkMaxLag() {
 459          wfProfileIn( __METHOD__ );
 460          $maxLag = $this->context->getRequest()->getVal( 'maxlag' );
 461          if ( !is_null( $maxLag ) ) {
 462              list( $host, $lag ) = wfGetLB()->getMaxLag();
 463              if ( $lag > $maxLag ) {
 464                  $resp = $this->context->getRequest()->response();
 465                  $resp->header( 'HTTP/1.1 503 Service Unavailable' );
 466                  $resp->header( 'Retry-After: ' . max( intval( $maxLag ), 5 ) );
 467                  $resp->header( 'X-Database-Lag: ' . intval( $lag ) );
 468                  $resp->header( 'Content-Type: text/plain' );
 469                  if ( $this->config->get( 'ShowHostnames' ) ) {
 470                      echo "Waiting for $host: $lag seconds lagged\n";
 471                  } else {
 472                      echo "Waiting for a database server: $lag seconds lagged\n";
 473                  }
 474  
 475                  wfProfileOut( __METHOD__ );
 476  
 477                  exit;
 478              }
 479          }
 480          wfProfileOut( __METHOD__ );
 481          return true;
 482      }
 483  
 484  	private function main() {
 485          global $wgTitle;
 486  
 487          wfProfileIn( __METHOD__ );
 488  
 489          $request = $this->context->getRequest();
 490  
 491          // Send Ajax requests to the Ajax dispatcher.
 492          if ( $this->config->get( 'UseAjax' ) && $request->getVal( 'action', 'view' ) == 'ajax' ) {
 493  
 494              // Set a dummy title, because $wgTitle == null might break things
 495              $title = Title::makeTitle( NS_MAIN, 'AJAX' );
 496              $this->context->setTitle( $title );
 497              $wgTitle = $title;
 498  
 499              $dispatcher = new AjaxDispatcher( $this->config );
 500              $dispatcher->performAction( $this->context->getUser() );
 501              wfProfileOut( __METHOD__ );
 502              return;
 503          }
 504  
 505          // Get title from request parameters,
 506          // is set on the fly by parseTitle the first time.
 507          $title = $this->getTitle();
 508          $action = $this->getAction();
 509          $wgTitle = $title;
 510  
 511          // If the user has forceHTTPS set to true, or if the user
 512          // is in a group requiring HTTPS, or if they have the HTTPS
 513          // preference set, redirect them to HTTPS.
 514          // Note: Do this after $wgTitle is setup, otherwise the hooks run from
 515          // isLoggedIn() will do all sorts of weird stuff.
 516          if (
 517              $request->getProtocol() == 'http' &&
 518              (
 519                  $request->getCookie( 'forceHTTPS', '' ) ||
 520                  // check for prefixed version for currently logged in users
 521                  $request->getCookie( 'forceHTTPS' ) ||
 522                  // Avoid checking the user and groups unless it's enabled.
 523                  (
 524                      $this->context->getUser()->isLoggedIn()
 525                      && $this->context->getUser()->requiresHTTPS()
 526                  )
 527              )
 528          ) {
 529              $oldUrl = $request->getFullRequestURL();
 530              $redirUrl = preg_replace( '#^http://#', 'https://', $oldUrl );
 531  
 532              // ATTENTION: This hook is likely to be removed soon due to overall design of the system.
 533              if ( wfRunHooks( 'BeforeHttpsRedirect', array( $this->context, &$redirUrl ) ) ) {
 534  
 535                  if ( $request->wasPosted() ) {
 536                      // This is weird and we'd hope it almost never happens. This
 537                      // means that a POST came in via HTTP and policy requires us
 538                      // redirecting to HTTPS. It's likely such a request is going
 539                      // to fail due to post data being lost, but let's try anyway
 540                      // and just log the instance.
 541                      //
 542                      // @todo FIXME: See if we could issue a 307 or 308 here, need
 543                      // to see how clients (automated & browser) behave when we do
 544                      wfDebugLog( 'RedirectedPosts', "Redirected from HTTP to HTTPS: $oldUrl" );
 545                  }
 546                  // Setup dummy Title, otherwise OutputPage::redirect will fail
 547                  $title = Title::newFromText( NS_MAIN, 'REDIR' );
 548                  $this->context->setTitle( $title );
 549                  $output = $this->context->getOutput();
 550                  // Since we only do this redir to change proto, always send a vary header
 551                  $output->addVaryHeader( 'X-Forwarded-Proto' );
 552                  $output->redirect( $redirUrl );
 553                  $output->output();
 554                  wfProfileOut( __METHOD__ );
 555                  return;
 556              }
 557          }
 558  
 559          if ( $this->config->get( 'UseFileCache' ) && $title->getNamespace() >= 0 ) {
 560              wfProfileIn( 'main-try-filecache' );
 561              if ( HTMLFileCache::useFileCache( $this->context ) ) {
 562                  // Try low-level file cache hit
 563                  $cache = new HTMLFileCache( $title, $action );
 564                  if ( $cache->isCacheGood( /* Assume up to date */ ) ) {
 565                      // Check incoming headers to see if client has this cached
 566                      $timestamp = $cache->cacheTimestamp();
 567                      if ( !$this->context->getOutput()->checkLastModified( $timestamp ) ) {
 568                          $cache->loadFromFileCache( $this->context );
 569                      }
 570                      // Do any stats increment/watchlist stuff
 571                      // Assume we're viewing the latest revision (this should always be the case with file cache)
 572                      $this->context->getWikiPage()->doViewUpdates( $this->context->getUser() );
 573                      // Tell OutputPage that output is taken care of
 574                      $this->context->getOutput()->disable();
 575                      wfProfileOut( 'main-try-filecache' );
 576                      wfProfileOut( __METHOD__ );
 577                      return;
 578                  }
 579              }
 580              wfProfileOut( 'main-try-filecache' );
 581          }
 582  
 583          // Actually do the work of the request and build up any output
 584          $this->performRequest();
 585  
 586          // Either all DB and deferred updates should happen or none.
 587          // The later should not be cancelled due to client disconnect.
 588          ignore_user_abort( true );
 589          // Now commit any transactions, so that unreported errors after
 590          // output() don't roll back the whole DB transaction
 591          wfGetLBFactory()->commitMasterChanges();
 592  
 593          // Output everything!
 594          $this->context->getOutput()->output();
 595  
 596          wfProfileOut( __METHOD__ );
 597      }
 598  
 599      /**
 600       * Ends this task peacefully
 601       */
 602  	public function restInPeace() {
 603          // Do any deferred jobs
 604          DeferredUpdates::doUpdates( 'commit' );
 605  
 606          // Log profiling data, e.g. in the database or UDP
 607          wfLogProfilingData();
 608  
 609          // Commit and close up!
 610          $factory = wfGetLBFactory();
 611          $factory->commitMasterChanges();
 612          $factory->shutdown();
 613  
 614          wfDebug( "Request ended normally\n" );
 615      }
 616  
 617      /**
 618       * Potentially open a socket and sent an HTTP request back to the server
 619       * to run a specified number of jobs. This registers a callback to cleanup
 620       * the socket once it's done.
 621       */
 622  	protected function triggerJobs() {
 623          $jobRunRate = $this->config->get( 'JobRunRate' );
 624          if ( $jobRunRate <= 0 || wfReadOnly() ) {
 625              return;
 626          } elseif ( $this->getTitle()->isSpecial( 'RunJobs' ) ) {
 627              return; // recursion guard
 628          }
 629  
 630          $section = new ProfileSection( __METHOD__ );
 631  
 632          if ( $jobRunRate < 1 ) {
 633              $max = mt_getrandmax();
 634              if ( mt_rand( 0, $max ) > $max * $jobRunRate ) {
 635                  return; // the higher the job run rate, the less likely we return here
 636              }
 637              $n = 1;
 638          } else {
 639              $n = intval( $jobRunRate );
 640          }
 641  
 642          if ( !$this->config->get( 'RunJobsAsync' ) ) {
 643              // Fall back to running the job here while the user waits
 644              $runner = new JobRunner();
 645              $runner->run( array( 'maxJobs'  => $n ) );
 646              return;
 647          }
 648  
 649          try {
 650              if ( !JobQueueGroup::singleton()->queuesHaveJobs( JobQueueGroup::TYPE_DEFAULT ) ) {
 651                  return; // do not send request if there are probably no jobs
 652              }
 653          } catch ( JobQueueError $e ) {
 654              MWExceptionHandler::logException( $e );
 655              return; // do not make the site unavailable
 656          }
 657  
 658          $query = array( 'title' => 'Special:RunJobs',
 659              'tasks' => 'jobs', 'maxjobs' => $n, 'sigexpiry' => time() + 5 );
 660          $query['signature'] = SpecialRunJobs::getQuerySignature(
 661              $query, $this->config->get( 'SecretKey' ) );
 662  
 663          $errno = $errstr = null;
 664          $info = wfParseUrl( $this->config->get( 'Server' ) );
 665          wfSuppressWarnings();
 666          $sock = fsockopen(
 667              $info['host'],
 668              isset( $info['port'] ) ? $info['port'] : 80,
 669              $errno,
 670              $errstr,
 671              // If it takes more than 100ms to connect to ourselves there
 672              // is a problem elsewhere.
 673              0.1
 674          );
 675          wfRestoreWarnings();
 676          if ( !$sock ) {
 677              wfDebugLog( 'runJobs', "Failed to start cron API (socket error $errno): $errstr\n" );
 678              // Fall back to running the job here while the user waits
 679              $runner = new JobRunner();
 680              $runner->run( array( 'maxJobs'  => $n ) );
 681              return;
 682          }
 683  
 684          $url = wfAppendQuery( wfScript( 'index' ), $query );
 685          $req = "POST $url HTTP/1.1\r\nHost: {$info['host']}\r\nConnection: Close\r\nContent-Length: 0\r\n\r\n";
 686  
 687          wfDebugLog( 'runJobs', "Running $n job(s) via '$url'\n" );
 688          // Send a cron API request to be performed in the background.
 689          // Give up if this takes too long to send (which should be rare).
 690          stream_set_timeout( $sock, 1 );
 691          $bytes = fwrite( $sock, $req );
 692          if ( $bytes !== strlen( $req ) ) {
 693              wfDebugLog( 'runJobs', "Failed to start cron API (socket write error)\n" );
 694          } else {
 695              // Do not wait for the response (the script should handle client aborts).
 696              // Make sure that we don't close before that script reaches ignore_user_abort().
 697              $status = fgets( $sock );
 698              if ( !preg_match( '#^HTTP/\d\.\d 202 #', $status ) ) {
 699                  wfDebugLog( 'runJobs', "Failed to start cron API: received '$status'\n" );
 700              }
 701          }
 702          fclose( $sock );
 703      }
 704  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1