| Current File : /home/jvzmxxx/wiki1/extensions/Flow/includes/Actions/PurgeAction.php |
<?php
namespace Flow\Actions;
use BagOStuff;
use Flow\Container;
use Flow\Data\ManagerGroup;
use Flow\Data\Pager\Pager;
use Flow\Formatter\TopicListQuery;
use Flow\Model\TopicListEntry;
use Flow\Model\UUID;
use Flow\Model\Workflow;
use Flow\WorkflowLoaderFactory;
use HashBagOStuff;
/**
* Extends the core Purge action to additionally purge flow specific cache
* keys related to the requested title.
*/
class PurgeAction extends \PurgeAction {
/**
* @var BagOStuff
*/
protected $realMemcache;
/**
* @var BagOStuff
*/
protected $hashBag;
/**
* {@inheritDoc}
*/
public function onSubmit( $data ) {
// Replace $c['memcache'] with a hash bag o stuff. This will look to the
// application layer like an empty cache, and as such it will populate this
// empty cache with all the cache keys required to reload this page.
// We then extract the complete list of keys updated from this hash bag o stuff
// and delete them from the real memcache.
// The container must be reset prior to this because the TitleSquidURLs hook
// will initialize memcache before this is run when UseSquid is enabled.
Container::reset();
$container = Container::getContainer();
$container->extend( 'memcache', function( $memcache, $c ) {
$c['memcache.purge_backup'] = $memcache;
return new HashBagOStuff;
} );
$this->hashBag = $container['memcache'];
$this->realMemcache = $container['memcache.purge_backup'];
if ( !parent::onSubmit( array() ) ) {
return false;
}
/** @var WorkflowLoaderFactory $loader */
$loader = $container['factory.loader.workflow'];
$workflow = $loader
->createWorkflowLoader( $this->page->getTitle() )
->getWorkflow();
switch( $workflow->getType() ) {
case 'discussion':
$this->fetchDiscussion( $workflow );
break;
case 'topic':
$this->fetchTopics( array( $workflow->getId()->getAlphadecimal() => $workflow->getId() ) );
break;
default:
throw new \MWException( 'Unknown workflow type: ' . $workflow->getType() );
}
// delete all the keys we just visited
$this->purgeCache();
return true;
}
/**
* Load the header and topics from the requested discussion. Does not return
* anything, the goal here is to populate $this->hashBag.
*
* @param Workflow $workflow
*/
protected function fetchDiscussion( Workflow $workflow ) {
$results = array();
$pagers = array();
/** @var ManagerGroup $storage */
$storage = Container::get( 'storage' );
// 'newest' sort order
$pagers[] = new Pager(
$storage->getStorage( 'TopicListEntry' ),
array( 'topic_list_id' => $workflow->getId() ),
array( 'pager-limit' => 499 )
);
// 'updated' sort order
$pagers[] = new Pager(
$storage->getStorage( 'TopicListEntry' ),
array( 'topic_list_id' => $workflow->getId() ),
array(
'pager-limit' => 499,
'sort' => 'workflow_last_update_timestamp',
'order' => 'desc',
)
);
// Based on Header::init.
$storage->find(
'Header',
array( 'rev_type_id' => $workflow->getId() ),
array( 'sort' => 'rev_id', 'order' => 'DESC', 'limit' => 1 )
);
foreach ( $pagers as $pager ) {
foreach ( $pager->getPage()->getResults() as $entry ) {
// use array key to de-duplicate
$results[$entry->getId()->getAlphadecimal()] = $entry->getId();
}
}
$this->fetchTopics( $results );
// purge the board history
$boardHistoryQuery = Container::get( 'query.board.history' );
$boardHistoryQuery->getResults( $workflow->getId(), 499 );
}
/**
* Load the requested topics. Does not return anything, the goal
* here is to populate $this->hashBag.
*
* @param UUID[] $results
*/
protected function fetchTopics( array $results ) {
// purge the revisions that make up the topic
/** @var TopicListQuery $topicListQuery */
$topicListQuery = Container::get( 'query.topiclist' );
$topicListQuery->getResults( $results );
$topicHistoryQuery = Container::get( 'query.topic.history' );
foreach ( $results as $id ) {
// purge the topic history
$topicHistoryQuery->getResults( $id, 499 );
}
}
/**
* Purge all keys written to $this->hashBag that match our cache prefix key.
*/
protected function purgeCache() {
$prefix = $this->cacheKeyPrefix();
$reflProp = new \ReflectionProperty( $this->hashBag, 'bag' );
$reflProp->setAccessible( true );
// Loop through all the cache keys we just populated and find the ones
// that contain our prefix.
$keys = array_filter(
array_keys( $reflProp->getValue( $this->hashBag ) ),
function( $key ) use( $prefix ) {
if ( strpos( $key, $prefix ) === 0 ) {
return true;
} else {
return false;
}
}
);
foreach ( $keys as $key ) {
$this->realMemcache->delete( $key );
}
return true;
}
/**
* @return string The id of the database being cached
*/
protected function cacheKeyPrefix() {
global $wgFlowDefaultWikiDb;
if ( $wgFlowDefaultWikiDb === false ) {
return wfWikiID();
} else {
return $wgFlowDefaultWikiDb;
}
}
}