Abuse Filter Parser updates

* Deprecate parseTokens in favour of a parse-as-you-go approach, faster and uses less memory.
* Display variables in lower_case so they aren't SHOUTING_AT_PEOPLE.
* Tell people if they try to use variables that don't exist, rather than silently returning NULL.
This commit is contained in:
Andrew Garrett 2009-02-11 20:00:33 +00:00
parent e1d2743d7a
commit 35e61feeb6
3 changed files with 72 additions and 76 deletions

View file

@ -60,42 +60,42 @@ class AbuseFilter {
'count(needle,haystack)' => 'count'
),
'vars' => array(
'ACCOUNTNAME' => 'accountname',
'ACTION' => 'action',
'ADDED_LINES' => 'addedlines',
'EDIT_DELTA' => 'delta',
'EDIT_DIFF' => 'diff',
'NEW_SIZE' => 'newsize',
'OLD_SIZE' => 'oldsize',
'REMOVED_LINES' => 'removedlines',
'SUMMARY' => 'summary',
'ARTICLE_ARTICLEID' => 'article-id',
'ARTICLE_NAMESPACE' => 'article-ns',
'ARTICLE_TEXT' => 'article-text',
'ARTICLE_PREFIXEDTEXT' => 'article-prefixedtext',
'MOVED_FROM_ARTICLEID' => 'movedfrom-id',
'MOVED_FROM_NAMESPACE' => 'movedfrom-ns',
'MOVED_FROM_TEXT' => 'movedfrom-text',
'MOVED_FROM_PREFIXEDTEXT' => 'movedfrom-prefixedtext',
'MOVED_TO_ARTICLEID' => 'movedto-id',
'MOVED_TO_NAMESPACE' => 'movedto-ns',
'MOVED_TO_TEXT' => 'movedto-text',
'MOVED_TO_PREFIXEDTEXT' => 'movedto-prefixedtext',
'USER_EDITCOUNT' => 'user-editcount',
'USER_AGE' => 'user-age',
'USER_NAME' => 'user-name',
'USER_GROUPS' => 'user-groups',
'USER_EMAILCONFIRM' => 'user-emailconfirm',
'OLD_WIKITEXT' => 'old-text',
'NEW_WIKITEXT' => 'new-text',
'ADDED_LINKS' => 'added-links',
'REMOVED_LINKS' => 'removed-links',
'ALL_LINKS' => 'all-links',
'NEW_TEXT' => 'new-text-stripped',
'NEW_HTML' => 'new-html',
'ARTICLE_RESTRICTIONS_edit' => 'restrictions-edit',
'ARTICLE_RESTRICTIONS_move' => 'restrictions-move',
'ARTICLE_RECENT_CONTRIBUTORS' => 'recent-contributors',
'accountname' => 'accountname',
'action' => 'action',
'added_lines' => 'addedlines',
'edit_delta' => 'delta',
'edit_diff' => 'diff',
'new_size' => 'newsize',
'old_size' => 'oldsize',
'removed_lines' => 'removedlines',
'summary' => 'summary',
'article_articleid' => 'article-id',
'article_namespace' => 'article-ns',
'article_text' => 'article-text',
'article_prefixedtext' => 'article-prefixedtext',
'moved_from_articleid' => 'movedfrom-id',
'moved_from_namespace' => 'movedfrom-ns',
'moved_from_text' => 'movedfrom-text',
'moved_from_prefixedtext' => 'movedfrom-prefixedtext',
'moved_to_articleid' => 'movedto-id',
'moved_to_namespace' => 'movedto-ns',
'moved_to_text' => 'movedto-text',
'moved_to_prefixedtext' => 'movedto-prefixedtext',
'user_editcount' => 'user-editcount',
'user_age' => 'user-age',
'user_name' => 'user-name',
'user_groups' => 'user-groups',
'user_emailconfirm' => 'user-emailconfirm',
'old_wikitext' => 'old-text',
'new_wikitext' => 'new-text',
'added_links' => 'added-links',
'removed_links' => 'removed-links',
'all_links' => 'all-links',
'new_text' => 'new-text-stripped',
'new_html' => 'new-html',
'article_restrictions_edit' => 'restrictions-edit',
'article_restrictions_move' => 'restrictions-move',
'article_recent_contributors' => 'recent-contributors',
),
);
@ -1116,6 +1116,8 @@ class AbuseFilter {
// Now, build the body of the table.
foreach( $vars as $key => $value ) {
$key = strtolower($key);
if ( !empty($variableMessageMappings[$key]) ) {
$mapping = $variableMessageMappings[$key];
$keyDisplay = wfMsgExt( "abusefilter-edit-builder-vars-$mapping", 'parseinline' ) .

View file

@ -297,6 +297,7 @@ Please ask a user with permission to add restricted actions to make the change f
'abusefilter-exception-unrecognisedtoken' => 'Unrecognised token "$2" at character $1.',
'abusefilter-exception-noparams' => 'No parameters given to function "$2" at character $1.',
'abusefilter-exception-dividebyzero' => 'Illegal attempt to divide $2 by zero at character $1.',
'abusefilter-exception-unrecognisedvar' => 'Unrecognised variable $2 at character $1',
// Actions
'abusefilter-action-throttle' => 'Throttle',

View file

@ -362,17 +362,15 @@ class AbuseFilterParser {
wfProfileOut( __METHOD__ );
}
protected function move( $shift = +1 ) {
$old = $this->mPos;
$this->mPos += $shift;
if( $this->mPos >= 0 && $this->mPos < count( $this->mTokens ) ) {
$this->mCur = $this->mTokens[$this->mPos];
return true;
}
else {
$this->mPos = $old;
return false;
}
protected function move( ) {
wfProfileIn( __METHOD__ );
list( $val, $type, $code, $offset ) =
self::nextToken( $this->mCode, $this->mPos );
$token = new AFPToken( $type, $val, $this->mPos );
$this->mPos = $offset;
wfProfileOut( __METHOD__ );
return $this->mCur = $token;
}
public function parse( $code ) {
@ -385,10 +383,15 @@ class AbuseFilterParser {
function intEval( $code ) {
wfProfileIn( __METHOD__ );
// Setup, resetting
$this->mCode = $code;
$this->mTokens = self::parseTokens( $code );
$this->mPos = 0;
$this->mCur = $this->mTokens[0];
$this->mLen = strlen( $code );
// Parse the first token
$this->move();
$result = new AFPData();
$this->doLevelEntry( $result );
wfProfileOut( __METHOD__ );
@ -641,8 +644,17 @@ class AbuseFilterParser {
$var = strtolower($tok);
if( isset( $this->mVars[$var] ) ) {
$result = $this->mVars[$var];
} else {
} elseif (
array_key_exists( strtolower($var),
AbuseFilter::$builderValues['vars'] )
) {
// If the variable is valid but not set, return null
$result = new AFPData();
} else {
// If the variable is invalid, throw an exception
throw new AFPUserVisibleException( 'unrecognisedvar',
$this->mCur->pos,
array( $var ) );
}
break;
case AFPToken::TString:
@ -662,7 +674,9 @@ class AbuseFilterParser {
elseif( $tok == "null" )
$result = new AFPData();
else
throw new AFPUserVisibleException( 'unrecognisedkeyword', $this->mCur->pos, array($tok) );
throw new AFPUserVisibleException( 'unrecognisedkeyword',
$this->mCur->pos,
array($tok) );
break;
case AFPToken::TBrace:
if( $this->mCur->value == ')' )
@ -681,38 +695,17 @@ class AbuseFilterParser {
/* End of levels */
public static function parseTokens( $code ) {
$r = array();
$len = strlen( $code );
$hash = md5(trim($code));
if (isset(self::$parserCache[$hash])) {
return self::$parserCache[$hash];
}
$offset = 0; // Where to next search
while( $tok = self::nextToken( $code, $offset ) ) {
$pos = $offset; // Start of the next token
list( $val, $type, $code, $offset ) = $tok;
$r[] = new AFPToken( $type, $val, $pos );
if( $type == AFPToken::TNone )
break;
}
return self::$parserCache[$hash] = $r;
}
static function nextToken( $code, $offset ) {
$tok = '';
static $lastInput = array();
// if ( $lastInput == array( $code, $offset ) ) {
// throw new AFPException( "Entered infinite loop. Offset $offset of $code" );
// }
if ( $lastInput == array( $code, $offset ) ) {
throw new AFPException( "Entered infinite loop. Offset $offset of $code" );
}
$lastInput = array( $code, $offset );
while( !empty( $code[$offset] ) && ctype_space( $code[$offset] ) )
$offset++;