Allow the parsers to return extra info

This is achieved by creating a new ParserStatus class. Aside from the
result of parse(), it contains whether the cache was warm. This can be
used to differentiate profiling data as part of T231112.

Another use case is returning non-fatal warnings (T269770).

Change-Id: Ifcbda861ce1a44bbe9bffba5b83cd9ef338a8dba
This commit is contained in:
Daimona Eaytoy 2019-09-15 17:48:13 +02:00
parent 93c477d4b8
commit 3e0c30ff92
7 changed files with 80 additions and 17 deletions

View file

@ -417,7 +417,7 @@ class AbuseFilterRunner {
$origExtraTime = AFComputedVariable::$profilingExtraTime;
$this->parser->setFilter( $filterName );
$result = $this->parser->checkConditions( $filter->getRules(), true, $filterName );
$result = $this->parser->checkConditions( $filter->getRules(), true, $filterName )->getResult();
$actualExtra = AFComputedVariable::$profilingExtraTime - $origExtraTime;
$timeTaken = 1000 * ( microtime( true ) - $startTime - $actualExtra );

View file

@ -68,7 +68,7 @@ class CheckMatch extends ApiBase {
$parser->setVariables( $vars );
$result = [
ApiResult::META_BC_BOOLS => [ 'result' ],
'result' => $parser->checkConditions( $params['filter'] ),
'result' => $parser->checkConditions( $params['filter'] )->getResult(),
];
$this->getResult()->addValue(

View file

@ -3,7 +3,7 @@
namespace MediaWiki\Extension\AbuseFilter\Parser;
use InvalidArgumentException;
use MWException;
use RuntimeException;
class AFPData {
// Datatypes
@ -420,7 +420,7 @@ class AFPData {
/** Convert shorteners */
/**
* @throws MWException
* @throws RuntimeException
* @return mixed
*/
public function toNative() {
@ -447,7 +447,7 @@ class AFPData {
return null;
default:
// @codeCoverageIgnoreStart
throw new MWException( "Unknown type" );
throw new RuntimeException( "Unknown type" );
// @codeCoverageIgnoreEnd
}
}

View file

@ -78,6 +78,7 @@ class AbuseFilterCachingParser extends AbuseFilterParser {
* @return AFPSyntaxTree
*/
private function getTree( $code ) : AFPSyntaxTree {
$this->fromCache = true;
return $this->cache->getWithSetCallback(
$this->cache->makeGlobalKey(
__CLASS__,
@ -86,6 +87,7 @@ class AbuseFilterCachingParser extends AbuseFilterParser {
),
BagOStuff::TTL_DAY,
function () use ( $code ) {
$this->fromCache = false;
$parser = new AFPTreeParser( $this->cache, $this->logger, $this->statsd, $this->keywordsManager );
$parser->setFilter( $this->mFilter );
return $parser->parse( $code );

View file

@ -9,7 +9,6 @@ use IBufferingStatsdDataFactory;
use InvalidArgumentException;
use Language;
use MediaWiki\Extension\AbuseFilter\KeywordsManager;
use MWException;
use NullStatsdDataFactory;
use Psr\Log\LoggerInterface;
use Sanitizer;
@ -62,6 +61,10 @@ class AbuseFilterParser extends AFPTransitionBase {
* @var BagOStuff Used to cache the AST (in CachingParser) and the tokens
*/
protected $cache;
/**
* @var bool Whether the AST was retrieved from cache (CachingParser only)
*/
protected $fromCache = false;
/**
* @var LoggerInterface Used for debugging
*/
@ -184,7 +187,7 @@ class AbuseFilterParser extends AFPTransitionBase {
/**
* @param int $val The amount to increase the conditions count of.
* @throws MWException
* @throws AFPException
*/
protected function raiseCondCount( $val = 1 ) {
global $wgAbuseFilterConditionLimit;
@ -192,7 +195,7 @@ class AbuseFilterParser extends AFPTransitionBase {
$this->mCondCount += $val;
if ( $this->condLimitEnabled && $this->mCondCount > $wgAbuseFilterConditionLimit ) {
throw new MWException( 'Condition limit reached.' );
throw new AFPException( 'Condition limit reached.' );
}
}
@ -267,15 +270,13 @@ class AbuseFilterParser extends AFPTransitionBase {
* @param string $conds
* @param bool $ignoreError
* @param string|null $filter The ID of the filter being parsed
* @return bool
* @throws Exception
* @return ParserStatus
* @throws AFPException
*/
public function checkConditions( string $conds, $ignoreError = true, $filter = null ) : bool {
try {
$result = $this->parse( $conds );
} catch ( Exception $excep ) {
$result = false;
public function checkConditions( string $conds, $ignoreError = true, $filter = null ) : ParserStatus {
$result = $this->parseDetailed( $conds );
$excep = $result->getException();
if ( $excep !== null ) {
if ( $excep instanceof AFPUserVisibleException ) {
$msg = $excep->getMessageForLogs();
$excep->setLocalizedMessage();
@ -391,6 +392,22 @@ class AbuseFilterParser extends AFPTransitionBase {
return $this->intEval( $code )->toBool();
}
/**
* Like self::parse(), but returns an object with additional info
* @param string $code
* @return ParserStatus
*/
public function parseDetailed( string $code ) : ParserStatus {
$excep = null;
try {
$res = $this->parse( $code );
} catch ( AFPException $excep ) {
$res = false;
} finally {
return new ParserStatus( $res, $this->fromCache, $excep );
}
}
/**
* @param string $filter
* @return string

View file

@ -0,0 +1,44 @@
<?php
namespace MediaWiki\Extension\AbuseFilter\Parser;
class ParserStatus {
/** @var bool */
private $result;
/** @var bool */
private $warmCache;
/** @var AFPException|null */
private $excep;
/**
* @param bool $result Whether the filter matched
* @param bool $warmCache Whether we retrieved the AST from cache
* @param AFPException|null $excep An exception thrown while parsing, or null if it parsed correctly
*/
public function __construct( bool $result, bool $warmCache, ?AFPException $excep ) {
$this->result = $result;
$this->warmCache = $warmCache;
$this->excep = $excep;
}
/**
* @return bool
*/
public function getResult() : bool {
return $this->result;
}
/**
* @return bool
*/
public function getWarmCache() : bool {
return $this->warmCache;
}
/**
* @return AFPException|null
*/
public function getException() : ?AFPException {
return $this->excep;
}
}

View file

@ -278,7 +278,7 @@ class AbuseFilterViewTestBatch extends AbuseFilterView {
}
$parser->setVariables( $vars );
$result = $parser->checkConditions( $this->testPattern );
$result = $parser->checkConditions( $this->testPattern )->getResult();
if ( $result || $this->mShowNegative ) {
// Stash result in RC item