1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
<?php
namespace MediaWiki\Extension\AbuseFilter;
use InvalidArgumentException;
use MediaWiki\Extension\AbuseFilter\Variables\VariableHolder;
use MediaWiki\Extension\AbuseFilter\Variables\VariablesManager;
use MediaWiki\Linker\LinkTarget;
use Psr\Log\LoggerInterface;
use Wikimedia\ObjectCache\BagOStuff;
use Wikimedia\Stats\IBufferingStatsdDataFactory;
/**
* Wrapper around cache for storing and retrieving data from edit stash
*/
class EditStashCache {
private const CACHE_VERSION = 'v5';
/** @var BagOStuff */
private $cache;
/** @var IBufferingStatsdDataFactory */
private $statsdDataFactory;
/** @var VariablesManager */
private $variablesManager;
/** @var LoggerInterface */
private $logger;
/** @var LinkTarget */
private $target;
/** @var string */
private $group;
/**
* @param BagOStuff $cache
* @param IBufferingStatsdDataFactory $statsdDataFactory
* @param VariablesManager $variablesManager
* @param LoggerInterface $logger
* @param LinkTarget $target
* @param string $group
*/
public function __construct(
BagOStuff $cache,
IBufferingStatsdDataFactory $statsdDataFactory,
VariablesManager $variablesManager,
LoggerInterface $logger,
LinkTarget $target,
string $group
) {
$this->cache = $cache;
$this->statsdDataFactory = $statsdDataFactory;
$this->variablesManager = $variablesManager;
$this->logger = $logger;
$this->target = $target;
$this->group = $group;
}
/**
* @param VariableHolder $vars For creating the key
* @param array $data Data to store
*/
public function store( VariableHolder $vars, array $data ): void {
$key = $this->getStashKey( $vars );
$this->cache->set( $key, $data, BagOStuff::TTL_MINUTE );
$this->logCache( 'store', $key );
}
/**
* Search the cache to find data for a previous execution done for the current edit.
*
* @param VariableHolder $vars For creating the key
* @return false|array False on cache miss, the array with data otherwise
*/
public function seek( VariableHolder $vars ) {
$key = $this->getStashKey( $vars );
$value = $this->cache->get( $key );
$status = $value !== false ? 'hit' : 'miss';
$this->logCache( $status, $key );
return $value;
}
/**
* Log cache operations related to stashed edits, i.e. store, hit and miss
*
* @param string $type Either 'store', 'hit' or 'miss'
* @param string $key The cache key used
* @throws InvalidArgumentException
*/
private function logCache( string $type, string $key ): void {
if ( !in_array( $type, [ 'store', 'hit', 'miss' ] ) ) {
// @codeCoverageIgnoreStart
throw new InvalidArgumentException( '$type must be either "store", "hit" or "miss"' );
// @codeCoverageIgnoreEnd
}
$this->logger->debug(
__METHOD__ . ": cache {logtype} for '{target}' (key {key}).",
[ 'logtype' => $type, 'target' => $this->target, 'key' => $key ]
);
$this->statsdDataFactory->increment( "abusefilter.check-stash.$type" );
}
/**
* Get the stash key for the current variables
*
* @param VariableHolder $vars
* @return string
*/
private function getStashKey( VariableHolder $vars ): string {
$inputVars = $this->variablesManager->exportNonLazyVars( $vars );
// Exclude noisy fields that have superficial changes
$excludedVars = [
'old_html' => true,
'new_html' => true,
'user_age' => true,
'timestamp' => true,
'page_age' => true,
'page_last_edit_age' => true,
];
$inputVars = array_diff_key( $inputVars, $excludedVars );
ksort( $inputVars );
$hash = md5( serialize( $inputVars ) );
return $this->cache->makeKey(
'abusefilter',
'check-stash',
$this->group,
$hash,
self::CACHE_VERSION
);
}
}
|