MediaWiki  REL1_23
Wiki.php
Go to the documentation of this file.
00001 <?php
00028 class MediaWiki {
00029 
00034     private $context;
00035 
00040     public function request( WebRequest $x = null ) {
00041         $old = $this->context->getRequest();
00042         $this->context->setRequest( $x );
00043         return $old;
00044     }
00045 
00050     public function output( OutputPage $x = null ) {
00051         $old = $this->context->getOutput();
00052         $this->context->setOutput( $x );
00053         return $old;
00054     }
00055 
00059     public function __construct( IContextSource $context = null ) {
00060         if ( !$context ) {
00061             $context = RequestContext::getMain();
00062         }
00063 
00064         $this->context = $context;
00065     }
00066 
00072     private function parseTitle() {
00073         global $wgContLang;
00074 
00075         $request = $this->context->getRequest();
00076         $curid = $request->getInt( 'curid' );
00077         $title = $request->getVal( 'title' );
00078         $action = $request->getVal( 'action', 'view' );
00079 
00080         if ( $request->getCheck( 'search' ) ) {
00081             // Compatibility with old search URLs which didn't use Special:Search
00082             // Just check for presence here, so blank requests still
00083             // show the search page when using ugly URLs (bug 8054).
00084             $ret = SpecialPage::getTitleFor( 'Search' );
00085         } elseif ( $curid ) {
00086             // URLs like this are generated by RC, because rc_title isn't always accurate
00087             $ret = Title::newFromID( $curid );
00088         } else {
00089             $ret = Title::newFromURL( $title );
00090             // Alias NS_MEDIA page URLs to NS_FILE...we only use NS_MEDIA
00091             // in wikitext links to tell Parser to make a direct file link
00092             if ( !is_null( $ret ) && $ret->getNamespace() == NS_MEDIA ) {
00093                 $ret = Title::makeTitle( NS_FILE, $ret->getDBkey() );
00094             }
00095             // Check variant links so that interwiki links don't have to worry
00096             // about the possible different language variants
00097             if ( count( $wgContLang->getVariants() ) > 1
00098                 && !is_null( $ret ) && $ret->getArticleID() == 0
00099             ) {
00100                 $wgContLang->findVariantLink( $title, $ret );
00101             }
00102         }
00103 
00104         // If title is not provided, always allow oldid and diff to set the title.
00105         // If title is provided, allow oldid and diff to override the title, unless
00106         // we are talking about a special page which might use these parameters for
00107         // other purposes.
00108         if ( $ret === null || !$ret->isSpecialPage() ) {
00109             // We can have urls with just ?diff=,?oldid= or even just ?diff=
00110             $oldid = $request->getInt( 'oldid' );
00111             $oldid = $oldid ? $oldid : $request->getInt( 'diff' );
00112             // Allow oldid to override a changed or missing title
00113             if ( $oldid ) {
00114                 $rev = Revision::newFromId( $oldid );
00115                 $ret = $rev ? $rev->getTitle() : $ret;
00116             }
00117         }
00118 
00119         // Use the main page as default title if nothing else has been provided
00120         if ( $ret === null && strval( $title ) === '' && !$request->getCheck( 'curid' ) && $action !== 'delete' ) {
00121             $ret = Title::newMainPage();
00122         }
00123 
00124         if ( $ret === null || ( $ret->getDBkey() == '' && !$ret->isExternal() ) ) {
00125             $ret = SpecialPage::getTitleFor( 'Badtitle' );
00126         }
00127 
00128         return $ret;
00129     }
00130 
00135     public function getTitle() {
00136         if ( $this->context->getTitle() === null ) {
00137             $this->context->setTitle( $this->parseTitle() );
00138         }
00139         return $this->context->getTitle();
00140     }
00141 
00147     public function getAction() {
00148         static $action = null;
00149 
00150         if ( $action === null ) {
00151             $action = Action::getActionName( $this->context );
00152         }
00153 
00154         return $action;
00155     }
00156 
00169     private function performRequest() {
00170         global $wgServer, $wgUsePathInfo, $wgTitle;
00171 
00172         wfProfileIn( __METHOD__ );
00173 
00174         $request = $this->context->getRequest();
00175         $requestTitle = $title = $this->context->getTitle();
00176         $output = $this->context->getOutput();
00177         $user = $this->context->getUser();
00178 
00179         if ( $request->getVal( 'printable' ) === 'yes' ) {
00180             $output->setPrintable();
00181         }
00182 
00183         $unused = null; // To pass it by reference
00184         wfRunHooks( 'BeforeInitialize', array( &$title, &$unused, &$output, &$user, $request, $this ) );
00185 
00186         // Invalid titles. Bug 21776: The interwikis must redirect even if the page name is empty.
00187         if ( is_null( $title ) || ( $title->getDBkey() == '' && !$title->isExternal() )
00188             || $title->isSpecial( 'Badtitle' )
00189         ) {
00190             $this->context->setTitle( SpecialPage::getTitleFor( 'Badtitle' ) );
00191             wfProfileOut( __METHOD__ );
00192             throw new BadTitleError();
00193         }
00194 
00195         // Check user's permissions to read this page.
00196         // We have to check here to catch special pages etc.
00197         // We will check again in Article::view().
00198         $permErrors = $title->getUserPermissionsErrors( 'read', $user );
00199         if ( count( $permErrors ) ) {
00200             // Bug 32276: allowing the skin to generate output with $wgTitle or
00201             // $this->context->title set to the input title would allow anonymous users to
00202             // determine whether a page exists, potentially leaking private data. In fact, the
00203             // curid and oldid request  parameters would allow page titles to be enumerated even
00204             // when they are not guessable. So we reset the title to Special:Badtitle before the
00205             // permissions error is displayed.
00206             //
00207             // The skin mostly uses $this->context->getTitle() these days, but some extensions
00208             // still use $wgTitle.
00209 
00210             $badTitle = SpecialPage::getTitleFor( 'Badtitle' );
00211             $this->context->setTitle( $badTitle );
00212             $wgTitle = $badTitle;
00213 
00214             wfProfileOut( __METHOD__ );
00215             throw new PermissionsError( 'read', $permErrors );
00216         }
00217 
00218         $pageView = false; // was an article or special page viewed?
00219 
00220         // Interwiki redirects
00221         if ( $title->isExternal() ) {
00222             $rdfrom = $request->getVal( 'rdfrom' );
00223             if ( $rdfrom ) {
00224                 $url = $title->getFullURL( array( 'rdfrom' => $rdfrom ) );
00225             } else {
00226                 $query = $request->getValues();
00227                 unset( $query['title'] );
00228                 $url = $title->getFullURL( $query );
00229             }
00230             // Check for a redirect loop
00231             if ( !preg_match( '/^' . preg_quote( $wgServer, '/' ) . '/', $url )
00232                 && $title->isLocal()
00233             ) {
00234                 // 301 so google et al report the target as the actual url.
00235                 $output->redirect( $url, 301 );
00236             } else {
00237                 $this->context->setTitle( SpecialPage::getTitleFor( 'Badtitle' ) );
00238                 wfProfileOut( __METHOD__ );
00239                 throw new BadTitleError();
00240             }
00241         // Redirect loops, no title in URL, $wgUsePathInfo URLs, and URLs with a variant
00242         } elseif ( $request->getVal( 'action', 'view' ) == 'view' && !$request->wasPosted()
00243             && ( $request->getVal( 'title' ) === null
00244                 || $title->getPrefixedDBkey() != $request->getVal( 'title' ) )
00245             && !count( $request->getValueNames( array( 'action', 'title' ) ) )
00246             && wfRunHooks( 'TestCanonicalRedirect', array( $request, $title, $output ) )
00247         ) {
00248             if ( $title->isSpecialPage() ) {
00249                 list( $name, $subpage ) = SpecialPageFactory::resolveAlias( $title->getDBkey() );
00250                 if ( $name ) {
00251                     $title = SpecialPage::getTitleFor( $name, $subpage );
00252                 }
00253             }
00254             $targetUrl = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
00255             // Redirect to canonical url, make it a 301 to allow caching
00256             if ( $targetUrl == $request->getFullRequestURL() ) {
00257                 $message = "Redirect loop detected!\n\n" .
00258                     "This means the wiki got confused about what page was " .
00259                     "requested; this sometimes happens when moving a wiki " .
00260                     "to a new server or changing the server configuration.\n\n";
00261 
00262                 if ( $wgUsePathInfo ) {
00263                     $message .= "The wiki is trying to interpret the page " .
00264                         "title from the URL path portion (PATH_INFO), which " .
00265                         "sometimes fails depending on the web server. Try " .
00266                         "setting \"\$wgUsePathInfo = false;\" in your " .
00267                         "LocalSettings.php, or check that \$wgArticlePath " .
00268                         "is correct.";
00269                 } else {
00270                     $message .= "Your web server was detected as possibly not " .
00271                         "supporting URL path components (PATH_INFO) correctly; " .
00272                         "check your LocalSettings.php for a customized " .
00273                         "\$wgArticlePath setting and/or toggle \$wgUsePathInfo " .
00274                         "to true.";
00275                 }
00276                 throw new HttpError( 500, $message );
00277             } else {
00278                 $output->setSquidMaxage( 1200 );
00279                 $output->redirect( $targetUrl, '301' );
00280             }
00281         // Special pages
00282         } elseif ( NS_SPECIAL == $title->getNamespace() ) {
00283             $pageView = true;
00284             // Actions that need to be made when we have a special pages
00285             SpecialPageFactory::executePath( $title, $this->context );
00286         } else {
00287             // ...otherwise treat it as an article view. The article
00288             // may be a redirect to another article or URL.
00289             $article = $this->initializeArticle();
00290             if ( is_object( $article ) ) {
00291                 $pageView = true;
00292                 $this->performAction( $article, $requestTitle );
00293             } elseif ( is_string( $article ) ) {
00294                 $output->redirect( $article );
00295             } else {
00296                 wfProfileOut( __METHOD__ );
00297                 throw new MWException( "Shouldn't happen: MediaWiki::initializeArticle()"
00298                     . " returned neither an object nor a URL" );
00299             }
00300         }
00301 
00302         if ( $pageView ) {
00303             // Promote user to any groups they meet the criteria for
00304             $user->addAutopromoteOnceGroups( 'onView' );
00305         }
00306 
00307         wfProfileOut( __METHOD__ );
00308     }
00309 
00316     private function initializeArticle() {
00317         global $wgDisableHardRedirects;
00318 
00319         wfProfileIn( __METHOD__ );
00320 
00321         $title = $this->context->getTitle();
00322         if ( $this->context->canUseWikiPage() ) {
00323             // Try to use request context wiki page, as there
00324             // is already data from db saved in per process
00325             // cache there from this->getAction() call.
00326             $page = $this->context->getWikiPage();
00327             $article = Article::newFromWikiPage( $page, $this->context );
00328         } else {
00329             // This case should not happen, but just in case.
00330             $article = Article::newFromTitle( $title, $this->context );
00331             $this->context->setWikiPage( $article->getPage() );
00332         }
00333 
00334         // NS_MEDIAWIKI has no redirects.
00335         // It is also used for CSS/JS, so performance matters here...
00336         if ( $title->getNamespace() == NS_MEDIAWIKI ) {
00337             wfProfileOut( __METHOD__ );
00338             return $article;
00339         }
00340 
00341         $request = $this->context->getRequest();
00342 
00343         // Namespace might change when using redirects
00344         // Check for redirects ...
00345         $action = $request->getVal( 'action', 'view' );
00346         $file = ( $title->getNamespace() == NS_FILE ) ? $article->getFile() : null;
00347         if ( ( $action == 'view' || $action == 'render' ) // ... for actions that show content
00348             && !$request->getVal( 'oldid' ) // ... and are not old revisions
00349             && !$request->getVal( 'diff' ) // ... and not when showing diff
00350             && $request->getVal( 'redirect' ) != 'no' // ... unless explicitly told not to
00351             // ... and the article is not a non-redirect image page with associated file
00352             && !( is_object( $file ) && $file->exists() && !$file->getRedirected() )
00353         ) {
00354             // Give extensions a change to ignore/handle redirects as needed
00355             $ignoreRedirect = $target = false;
00356 
00357             wfRunHooks( 'InitializeArticleMaybeRedirect',
00358                 array( &$title, &$request, &$ignoreRedirect, &$target, &$article ) );
00359 
00360             // Follow redirects only for... redirects.
00361             // If $target is set, then a hook wanted to redirect.
00362             if ( !$ignoreRedirect && ( $target || $article->isRedirect() ) ) {
00363                 // Is the target already set by an extension?
00364                 $target = $target ? $target : $article->followRedirect();
00365                 if ( is_string( $target ) ) {
00366                     if ( !$wgDisableHardRedirects ) {
00367                         // we'll need to redirect
00368                         wfProfileOut( __METHOD__ );
00369                         return $target;
00370                     }
00371                 }
00372                 if ( is_object( $target ) ) {
00373                     // Rewrite environment to redirected article
00374                     $rarticle = Article::newFromTitle( $target, $this->context );
00375                     $rarticle->loadPageData();
00376                     if ( $rarticle->exists() || ( is_object( $file ) && !$file->isLocal() ) ) {
00377                         $rarticle->setRedirectedFrom( $title );
00378                         $article = $rarticle;
00379                         $this->context->setTitle( $target );
00380                         $this->context->setWikiPage( $article->getPage() );
00381                     }
00382                 }
00383             } else {
00384                 $this->context->setTitle( $article->getTitle() );
00385                 $this->context->setWikiPage( $article->getPage() );
00386             }
00387         }
00388 
00389         wfProfileOut( __METHOD__ );
00390         return $article;
00391     }
00392 
00399     private function performAction( Page $page, Title $requestTitle ) {
00400         global $wgUseSquid, $wgSquidMaxage;
00401 
00402         wfProfileIn( __METHOD__ );
00403 
00404         $request = $this->context->getRequest();
00405         $output = $this->context->getOutput();
00406         $title = $this->context->getTitle();
00407         $user = $this->context->getUser();
00408 
00409         if ( !wfRunHooks( 'MediaWikiPerformAction',
00410                 array( $output, $page, $title, $user, $request, $this ) )
00411         ) {
00412             wfProfileOut( __METHOD__ );
00413             return;
00414         }
00415 
00416         $act = $this->getAction();
00417 
00418         $action = Action::factory( $act, $page, $this->context );
00419 
00420         if ( $action instanceof Action ) {
00421             # Let Squid cache things if we can purge them.
00422             if ( $wgUseSquid &&
00423                 in_array( $request->getFullRequestURL(), $requestTitle->getSquidURLs() )
00424             ) {
00425                 $output->setSquidMaxage( $wgSquidMaxage );
00426             }
00427 
00428             $action->show();
00429             wfProfileOut( __METHOD__ );
00430             return;
00431         }
00432 
00433         if ( wfRunHooks( 'UnknownAction', array( $request->getVal( 'action', 'view' ), $page ) ) ) {
00434             $output->showErrorPage( 'nosuchaction', 'nosuchactiontext' );
00435         }
00436 
00437         wfProfileOut( __METHOD__ );
00438     }
00439 
00444     public function run() {
00445         try {
00446             $this->checkMaxLag();
00447             $this->main();
00448             if ( function_exists( 'fastcgi_finish_request' ) ) {
00449                 fastcgi_finish_request();
00450             }
00451             $this->triggerJobs();
00452             $this->restInPeace();
00453         } catch ( Exception $e ) {
00454             MWExceptionHandler::handle( $e );
00455         }
00456     }
00457 
00463     private function checkMaxLag() {
00464         global $wgShowHostnames;
00465 
00466         wfProfileIn( __METHOD__ );
00467         $maxLag = $this->context->getRequest()->getVal( 'maxlag' );
00468         if ( !is_null( $maxLag ) ) {
00469             list( $host, $lag ) = wfGetLB()->getMaxLag();
00470             if ( $lag > $maxLag ) {
00471                 $resp = $this->context->getRequest()->response();
00472                 $resp->header( 'HTTP/1.1 503 Service Unavailable' );
00473                 $resp->header( 'Retry-After: ' . max( intval( $maxLag ), 5 ) );
00474                 $resp->header( 'X-Database-Lag: ' . intval( $lag ) );
00475                 $resp->header( 'Content-Type: text/plain' );
00476                 if ( $wgShowHostnames ) {
00477                     echo "Waiting for $host: $lag seconds lagged\n";
00478                 } else {
00479                     echo "Waiting for a database server: $lag seconds lagged\n";
00480                 }
00481 
00482                 wfProfileOut( __METHOD__ );
00483 
00484                 exit;
00485             }
00486         }
00487         wfProfileOut( __METHOD__ );
00488         return true;
00489     }
00490 
00491     private function main() {
00492         global $wgUseFileCache, $wgTitle, $wgUseAjax;
00493 
00494         wfProfileIn( __METHOD__ );
00495 
00496         $request = $this->context->getRequest();
00497 
00498         // Send Ajax requests to the Ajax dispatcher.
00499         if ( $wgUseAjax && $request->getVal( 'action', 'view' ) == 'ajax' ) {
00500 
00501             // Set a dummy title, because $wgTitle == null might break things
00502             $title = Title::makeTitle( NS_MAIN, 'AJAX' );
00503             $this->context->setTitle( $title );
00504             $wgTitle = $title;
00505 
00506             $dispatcher = new AjaxDispatcher();
00507             $dispatcher->performAction();
00508             wfProfileOut( __METHOD__ );
00509             return;
00510         }
00511 
00512         // Get title from request parameters,
00513         // is set on the fly by parseTitle the first time.
00514         $title = $this->getTitle();
00515         $action = $this->getAction();
00516         $wgTitle = $title;
00517 
00518         // If the user has forceHTTPS set to true, or if the user
00519         // is in a group requiring HTTPS, or if they have the HTTPS
00520         // preference set, redirect them to HTTPS.
00521         // Note: Do this after $wgTitle is setup, otherwise the hooks run from
00522         // isLoggedIn() will do all sorts of weird stuff.
00523         if (
00524             (
00525                 $request->getCookie( 'forceHTTPS', '' ) ||
00526                 // check for prefixed version for currently logged in users
00527                 $request->getCookie( 'forceHTTPS' ) ||
00528                 // Avoid checking the user and groups unless it's enabled.
00529                 (
00530                     $this->context->getUser()->isLoggedIn()
00531                     && $this->context->getUser()->requiresHTTPS()
00532                 )
00533             ) &&
00534             $request->getProtocol() == 'http'
00535         ) {
00536             $oldUrl = $request->getFullRequestURL();
00537             $redirUrl = str_replace( 'http://', 'https://', $oldUrl );
00538 
00539             if ( $request->wasPosted() ) {
00540                 // This is weird and we'd hope it almost never happens. This
00541                 // means that a POST came in via HTTP and policy requires us
00542                 // redirecting to HTTPS. It's likely such a request is going
00543                 // to fail due to post data being lost, but let's try anyway
00544                 // and just log the instance.
00545                 //
00546                 // @todo @fixme See if we could issue a 307 or 308 here, need
00547                 // to see how clients (automated & browser) behave when we do
00548                 wfDebugLog( 'RedirectedPosts', "Redirected from HTTP to HTTPS: $oldUrl" );
00549             }
00550 
00551             // Setup dummy Title, otherwise OutputPage::redirect will fail
00552             $title = Title::newFromText( NS_MAIN, 'REDIR' );
00553             $this->context->setTitle( $title );
00554             $output = $this->context->getOutput();
00555             // Since we only do this redir to change proto, always send a vary header
00556             $output->addVaryHeader( 'X-Forwarded-Proto' );
00557             $output->redirect( $redirUrl );
00558             $output->output();
00559             wfProfileOut( __METHOD__ );
00560             return;
00561         }
00562 
00563         if ( $wgUseFileCache && $title->getNamespace() >= 0 ) {
00564             wfProfileIn( 'main-try-filecache' );
00565             if ( HTMLFileCache::useFileCache( $this->context ) ) {
00566                 // Try low-level file cache hit
00567                 $cache = HTMLFileCache::newFromTitle( $title, $action );
00568                 if ( $cache->isCacheGood( /* Assume up to date */ ) ) {
00569                     // Check incoming headers to see if client has this cached
00570                     $timestamp = $cache->cacheTimestamp();
00571                     if ( !$this->context->getOutput()->checkLastModified( $timestamp ) ) {
00572                         $cache->loadFromFileCache( $this->context );
00573                     }
00574                     // Do any stats increment/watchlist stuff
00575                     // Assume we're viewing the latest revision (this should always be the case with file cache)
00576                     $this->context->getWikiPage()->doViewUpdates( $this->context->getUser() );
00577                     // Tell OutputPage that output is taken care of
00578                     $this->context->getOutput()->disable();
00579                     wfProfileOut( 'main-try-filecache' );
00580                     wfProfileOut( __METHOD__ );
00581                     return;
00582                 }
00583             }
00584             wfProfileOut( 'main-try-filecache' );
00585         }
00586 
00587         // Actually do the work of the request and build up any output
00588         $this->performRequest();
00589 
00590         // Either all DB and deferred updates should happen or none.
00591         // The later should not be cancelled due to client disconnect.
00592         ignore_user_abort( true );
00593         // Now commit any transactions, so that unreported errors after
00594         // output() don't roll back the whole DB transaction
00595         wfGetLBFactory()->commitMasterChanges();
00596 
00597         // Output everything!
00598         $this->context->getOutput()->output();
00599 
00600         wfProfileOut( __METHOD__ );
00601     }
00602 
00606     public function restInPeace() {
00607         // Do any deferred jobs
00608         DeferredUpdates::doUpdates( 'commit' );
00609 
00610         // Log profiling data, e.g. in the database or UDP
00611         wfLogProfilingData();
00612 
00613         // Commit and close up!
00614         $factory = wfGetLBFactory();
00615         $factory->commitMasterChanges();
00616         $factory->shutdown();
00617 
00618         wfDebug( "Request ended normally\n" );
00619     }
00620 
00626     protected function triggerJobs() {
00627         global $wgJobRunRate, $wgServer, $wgRunJobsAsync;
00628 
00629         if ( $wgJobRunRate <= 0 || wfReadOnly() ) {
00630             return;
00631         } elseif ( $this->getTitle()->isSpecial( 'RunJobs' ) ) {
00632             return; // recursion guard
00633         }
00634 
00635         $section = new ProfileSection( __METHOD__ );
00636 
00637         if ( $wgJobRunRate < 1 ) {
00638             $max = mt_getrandmax();
00639             if ( mt_rand( 0, $max ) > $max * $wgJobRunRate ) {
00640                 return; // the higher $wgJobRunRate, the less likely we return here
00641             }
00642             $n = 1;
00643         } else {
00644             $n = intval( $wgJobRunRate );
00645         }
00646 
00647         if ( !$wgRunJobsAsync ) {
00648             // If running jobs asynchronously has been disabled, run the job here
00649             // while the user waits
00650             SpecialRunJobs::executeJobs( $n );
00651             return;
00652         }
00653 
00654         if ( !JobQueueGroup::singleton()->queuesHaveJobs( JobQueueGroup::TYPE_DEFAULT ) ) {
00655             return; // do not send request if there are probably no jobs
00656         }
00657 
00658         $query = array( 'title' => 'Special:RunJobs',
00659             'tasks' => 'jobs', 'maxjobs' => $n, 'sigexpiry' => time() + 5 );
00660         $query['signature'] = SpecialRunJobs::getQuerySignature( $query );
00661 
00662         $errno = $errstr = null;
00663         $info = wfParseUrl( $wgServer );
00664         wfSuppressWarnings();
00665         $sock = fsockopen(
00666             $info['host'],
00667             isset( $info['port'] ) ? $info['port'] : 80,
00668             $errno,
00669             $errstr,
00670             // If it takes more than 100ms to connect to ourselves there
00671             // is a problem elsewhere.
00672             0.1
00673         );
00674         wfRestoreWarnings();
00675         if ( !$sock ) {
00676             wfDebugLog( 'runJobs', "Failed to start cron API (socket error $errno): $errstr\n" );
00677             // Fall back to running the job here while the user waits
00678             SpecialRunJobs::executeJobs( $n );
00679             return;
00680         }
00681 
00682         $url = wfAppendQuery( wfScript( 'index' ), $query );
00683         $req = "POST $url HTTP/1.1\r\nHost: {$info['host']}\r\nConnection: Close\r\n\r\n";
00684 
00685         wfDebugLog( 'runJobs', "Running $n job(s) via '$url'\n" );
00686         // Send a cron API request to be performed in the background.
00687         // Give up if this takes too long to send (which should be rare).
00688         stream_set_timeout( $sock, 1 );
00689         $bytes = fwrite( $sock, $req );
00690         if ( $bytes !== strlen( $req ) ) {
00691             wfDebugLog( 'runJobs', "Failed to start cron API (socket write error)\n" );
00692         } else {
00693             // Do not wait for the response (the script should handle client aborts).
00694             // Make sure that we don't close before that script reaches ignore_user_abort().
00695             $status = fgets( $sock );
00696             if ( !preg_match( '#^HTTP/\d\.\d 202 #', $status ) ) {
00697                 wfDebugLog( 'runJobs', "Failed to start cron API: received '$status'\n" );
00698             }
00699         }
00700         fclose( $sock );
00701     }
00702 }