- /**
- * Generates feed's content
- *
- * @param ChannelFeed $feed ChannelFeed subclass object (generally the one returned
- * by getFeedObject())
- * @param ResultWrapper $rows ResultWrapper object with rows in recentchanges table
- * @param int $lastmod Timestamp of the last item in the recentchanges table (only
- * used for the cache key)
- * @param FormOptions $opts As in SpecialRecentChanges::getDefaultOptions()
- * @return null|bool True or null
- */
- public function execute( $feed, $rows, $lastmod, $opts ) {
- global $wgLang, $wgRenderHashAppend;
-
- if ( !FeedUtils::checkFeedOutput( $this->format ) ) {
- return null;
- }
-
- $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
- $optionsHash = md5( serialize( $opts->getAllValues() ) ) . $wgRenderHashAppend;
- $timekey = $cache->makeKey(
- $this->type, $this->format, $wgLang->getCode(), $optionsHash, 'timestamp' );
- $key = $cache->makeKey( $this->type, $this->format, $wgLang->getCode(), $optionsHash );
-
- FeedUtils::checkPurge( $timekey, $key );
-
- /**
- * Bumping around loading up diffs can be pretty slow, so where
- * possible we want to cache the feed output so the next visitor
- * gets it quick too.
- */
- $cachedFeed = $this->loadFromCache( $lastmod, $timekey, $key );
- if ( is_string( $cachedFeed ) ) {
- wfDebug( "RC: Outputting cached feed\n" );
- $feed->httpHeaders();
- echo $cachedFeed;
- } else {
- wfDebug( "RC: rendering new feed and caching it\n" );
- ob_start();
- self::generateFeed( $rows, $feed );
- $cachedFeed = ob_get_contents();
- ob_end_flush();
- $this->saveToCache( $cachedFeed, $timekey, $key );
- }
- return true;
- }
-
- /**
- * Save to feed result to cache
- *
- * @param string $feed Feed's content
- * @param string $timekey Memcached key of the last modification
- * @param string $key Memcached key of the content
- */
- public function saveToCache( $feed, $timekey, $key ) {
- $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
- $cache->set( $key, $feed, $cache::TTL_DAY );
- $cache->set( $timekey, wfTimestamp( TS_MW ), $cache::TTL_DAY );
- }
-
- /**
- * Try to load the feed result from cache
- *
- * @param int $lastmod Timestamp of the last item in the recentchanges table
- * @param string $timekey Memcached key of the last modification
- * @param string $key Memcached key of the content
- * @return string|bool Feed's content on cache hit or false on cache miss
- */
- public function loadFromCache( $lastmod, $timekey, $key ) {
- global $wgFeedCacheTimeout, $wgOut;
-
- $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
- $feedLastmod = $cache->get( $timekey );
-
- if ( ( $wgFeedCacheTimeout > 0 ) && $feedLastmod ) {
- /**
- * If the cached feed was rendered very recently, we may
- * go ahead and use it even if there have been edits made
- * since it was rendered. This keeps a swarm of requests
- * from being too bad on a super-frequently edited wiki.
- */
-
- $feedAge = time() - wfTimestamp( TS_UNIX, $feedLastmod );
- $feedLastmodUnix = wfTimestamp( TS_UNIX, $feedLastmod );
- $lastmodUnix = wfTimestamp( TS_UNIX, $lastmod );
-
- if ( $feedAge < $wgFeedCacheTimeout || $feedLastmodUnix > $lastmodUnix ) {
- wfDebug( "RC: loading feed from cache ($key; $feedLastmod; $lastmod)...\n" );
- if ( $feedLastmodUnix < $lastmodUnix ) {
- $wgOut->setLastModified( $feedLastmod ); // T23916
- }
- return $cache->get( $key );
- } else {
- wfDebug( "RC: cached feed timestamp check failed ($feedLastmod; $lastmod)\n" );
- }
- }
- return false;
- }
-
- /**
- * Generate the feed items given a row from the database, printing the feed.
- * @param object $rows IDatabase resource with recentchanges rows
- * @param ChannelFeed &$feed
- */
- public static function generateFeed( $rows, &$feed ) {
- $items = self::buildItems( $rows );
- $feed->outHeader();
- foreach ( $items as $item ) {
- $feed->outItem( $item );
- }
- $feed->outFooter();
- }
-