Index: trunk/extensions/AbuseFilter/AbuseFilter.parser.php |
— | — | @@ -1,689 +0,0 @@ |
2 | | -<?php |
3 | | -if ( ! defined( 'MEDIAWIKI' ) ) |
4 | | - die(); |
5 | | -/** |
6 | | -Abuse filter parser. |
7 | | -Copyright (C) Victor Vasiliev, 2008. Based on ideas by Andrew Garrett Distributed under GNU GPL v2 terms. |
8 | | - |
9 | | -Types of token: |
10 | | -* T_NONE - special-purpose token |
11 | | -* T_BRACE - ( or ) |
12 | | -* T_COMMA - , |
13 | | -* T_OP - operator like + or ^ |
14 | | -* T_NUMBER - number |
15 | | -* T_STRING - string, in "" or '' |
16 | | -* T_KEYWORD - keyword |
17 | | -* T_ID - identifier |
18 | | - |
19 | | -Levels of parsing: |
20 | | -* Set (S) - ==, +=, etc. |
21 | | -* BoolOps (BO) - &, |, ^ |
22 | | -* CompOps (CO) - ==, !=, ===, !==, >, <, >=, <= |
23 | | -* SumRel (SR) - +, - |
24 | | -* MulRel (MR) - *, /, % |
25 | | -* Pow (P) - ** |
26 | | -* BoolNeg (BN) - ! operation |
27 | | -* SpecialOperators (SO) - in and like |
28 | | -* Unarys (U) - plus and minus in cases like -5 or -(2 * +2) |
29 | | -* Braces (B) - ( and ) |
30 | | -* Functions (F) |
31 | | -* Atom (A) - return value |
32 | | -*/ |
33 | | - |
34 | | -class AFPToken { |
35 | | - //Types of tken |
36 | | - const TNone = 'T_NONE'; |
37 | | - const TID = 'T_ID'; |
38 | | - const TKeyword = 'T_KEYWORD'; |
39 | | - const TString = 'T_STRING'; |
40 | | - const TNumber = 'T_NUMBER'; |
41 | | - const TOp = 'T_OP'; |
42 | | - const TBrace = 'T_BRACE'; |
43 | | - const TComma = 'T_COMMA'; |
44 | | - |
45 | | - var $type; |
46 | | - var $value; |
47 | | - var $pos; |
48 | | - |
49 | | - public function __construct( $type = self::TNone, $value = null, $pos = 0 ) { |
50 | | - $this->type = $type; |
51 | | - $this->value = $value; |
52 | | - $this->pos = $pos; |
53 | | - } |
54 | | -} |
55 | | - |
56 | | -class AFPData { |
57 | | - //Datatypes |
58 | | - const DNumber = 'number'; //any integer or double |
59 | | - const DString = 'string'; |
60 | | - const DNull = 'null'; |
61 | | - const DBool = 'bool'; |
62 | | - |
63 | | - var $type; |
64 | | - var $data; |
65 | | - |
66 | | - public function __construct( $type = self::DNull, $val = null ) { |
67 | | - $this->type = $type; |
68 | | - $this->data = $val; |
69 | | - } |
70 | | - |
71 | | - public static function newFromPHPVar( $var ) { |
72 | | - if( is_string( $var ) ) |
73 | | - return new AFPData( self::DString, $var ); |
74 | | - elseif( is_int( $var ) || is_float( $var ) ) |
75 | | - return new AFPData( self::DNumber, $var ); |
76 | | - elseif( is_bool( $var ) ) |
77 | | - return new AFPData( self::DBool, $var ); |
78 | | - elseif( is_null( $var ) ) |
79 | | - return new AFPData(); |
80 | | - else |
81 | | - throw new AFPException( "Data type " . gettype( $var ) . " is not supported by AbuseFilter" ); |
82 | | - } |
83 | | - |
84 | | - public function dup() { |
85 | | - return new AFPData( $this->type, $this->data ); |
86 | | - } |
87 | | - |
88 | | - public static function castTypes( $orig, $target ) { |
89 | | - if( $orig->type == $target ) |
90 | | - return $orig->dup(); |
91 | | - if( $target == self::DNull ) { |
92 | | - return new AFPData(); |
93 | | - } |
94 | | - if( $target == self::DBool ) { |
95 | | - return new AFPData( self::DBool, (bool)$orig->data ); |
96 | | - } |
97 | | - if( $target == self::DNumber ) { |
98 | | - return new AFPData( self::DNumber, doubleval( $orig->data ) ); |
99 | | - } |
100 | | - if( $target == self::DString ) { |
101 | | - return new AFPData( self::DString, strval( $orig->data ) ); |
102 | | - } |
103 | | - } |
104 | | - |
105 | | - public static function boolInvert( $value ) { |
106 | | - return new AFPData( self::DBool, !$value->toBool() ); |
107 | | - } |
108 | | - |
109 | | - public static function pow( $base, $exponent ) { |
110 | | - return new AFPData( self::DNumber, pow( $base->toNumber(), $exponent->toNumber() ) ); |
111 | | - } |
112 | | - |
113 | | - public static function keywordIn( $a, $b ) { |
114 | | - $a = $a->toString(); |
115 | | - $b = $b->toString(); |
116 | | - |
117 | | - if ($a == '' || $b == '') { |
118 | | - return new AFPData( self::DBool, false ); |
119 | | - } |
120 | | - |
121 | | - return new AFPData( self::DBool, in_string( $a, $b ) ); |
122 | | - } |
123 | | - |
124 | | - public static function keywordLike( $str, $regex ) { |
125 | | - $str = $str->toString(); |
126 | | - $regex = $regex->toString() . 'u'; //Append unicode modifier |
127 | | - wfSuppressWarnings(); |
128 | | - $result = preg_match( $regex, $str ); |
129 | | - wfRestoreWarnings(); |
130 | | - return new AFPData( self::DBool, (bool)$result ); |
131 | | - } |
132 | | - |
133 | | - public static function unaryMinus( $data ) { |
134 | | - return new AFPData( self::DNumber, $data->toNumber() ); |
135 | | - } |
136 | | - |
137 | | - public static function boolOp( $a, $b, $op ) { |
138 | | - $a = $a->toBool(); |
139 | | - $b = $b->toBool(); |
140 | | - if( $op == '|' ) |
141 | | - return new AFPData( self::DBool, $a || $b ); |
142 | | - if( $op == '&' ) |
143 | | - return new AFPData( self::DBool, $a && $b ); |
144 | | - if( $op == '^' ) |
145 | | - return new AFPData( self::DBool, $a xor $b ); |
146 | | - throw new AFPException( "Invalid boolean operation: {$op}" ); |
147 | | - } |
148 | | - |
149 | | - public static function compareOp( $a, $b, $op ) { |
150 | | - if( $op == '==' ) |
151 | | - return new AFPData( self::DBool, $a->toString() === $b->toString() ); |
152 | | - if( $op == '!=' ) |
153 | | - return new AFPData( self::DBool, $a->toString() !== $b->toString() ); |
154 | | - if( $op == '===' ) |
155 | | - return new AFPData( self::DBool, $a->data === $b->data && $a->type == $b->type ); |
156 | | - if( $op == '!==' ) |
157 | | - return new AFPData( self::DBool, $a->data !== $b->data || $a->type != $b->type ); |
158 | | - $a = $a->toString(); |
159 | | - $b = $b->toString(); |
160 | | - if( $op == '>' ) |
161 | | - return new AFPData( self::DBool, $a > $b ); |
162 | | - if( $op == '<' ) |
163 | | - return new AFPData( self::DBool, $a < $b ); |
164 | | - if( $op == '>=' ) |
165 | | - return new AFPData( self::DBool, $a >= $b ); |
166 | | - if( $op == '<=' ) |
167 | | - return new AFPData( self::DBool, $a <= $b ); |
168 | | - throw new AFPException( "Invalid comprasion operation: {$op}" ); |
169 | | - } |
170 | | - |
171 | | - public static function mulRel( $a, $b, $op ) { |
172 | | - $a = $a->toNumber(); |
173 | | - $b = $b->toNumber(); |
174 | | - if( $op == '*' ) |
175 | | - return new AFPData( self::DNumber, $a * $b ); |
176 | | - if( $op == '/' ) |
177 | | - return new AFPData( self::DNumber, $a / $b ); |
178 | | - if( $op == '%' ) |
179 | | - return new AFPData( self::DNumber, $a % $b ); |
180 | | - throw new AFPException( "Invalid multiplication-related operation: {$op}" ); |
181 | | - } |
182 | | - |
183 | | - public static function sum( $a, $b ) { |
184 | | - if( $a->type == self::DString || $b->type == self::DString ) |
185 | | - return new AFPData( self::DString, $a->toString() . $b->toString() ); |
186 | | - else |
187 | | - return new AFPData( self::DNumber, $a->toNumber() + $b->toNumber() ); |
188 | | - } |
189 | | - |
190 | | - public static function sub( $a, $b ) { |
191 | | - return new AFPData( self::DNumber, $a->toNumber() - $b->toNumber() ); |
192 | | - } |
193 | | - |
194 | | - /** Convert shorteners */ |
195 | | - public function toBool() { |
196 | | - return self::castTypes( $this, self::DBool )->data; |
197 | | - } |
198 | | - |
199 | | - public function toString() { |
200 | | - return self::castTypes( $this, self::DString )->data; |
201 | | - } |
202 | | - |
203 | | - public function toNumber() { |
204 | | - return self::castTypes( $this, self::DNumber )->data; |
205 | | - } |
206 | | -} |
207 | | - |
208 | | -class AFPException extends MWException {} |
209 | | - |
210 | | -class AbuseFilterParser { |
211 | | - var $mParams, $mVars, $mCode, $mTokens, $mPos, $mCur; |
212 | | - |
213 | | - // length,lcase,ccnorm,rmdoubles,specialratio,rmspecials,norm,count |
214 | | - static $mFunctions = array( |
215 | | - 'lcase' => 'funcLc', |
216 | | - 'length' => 'funcLen', |
217 | | -// 'norm' => 'funcNorm', |
218 | | -// 'ccnorm' => 'funcSimpleNorm', |
219 | | -// 'specialratio' => 'funcSpecialRatio', |
220 | | -// 'rmspecials' => 'funcRmSpecials', |
221 | | -// 'count' => 'funcCount' |
222 | | - ); |
223 | | - static $mOps = array( |
224 | | - '!', '*', '**', '/', '+', '-', '%', '&', '|', '^', |
225 | | - '<', '>', '>=', '<=', '==', '!=', '=', '===', '!==', |
226 | | - ); |
227 | | - static $mKeywords = array( |
228 | | - 'in', 'like', 'true', 'false', 'null', |
229 | | - ); |
230 | | - |
231 | | - static $parserCache = array(); |
232 | | - |
233 | | - static $funcCache = array(); |
234 | | - |
235 | | - public function __construct() { |
236 | | - $this->resetState(); |
237 | | - } |
238 | | - |
239 | | - public function resetState() { |
240 | | - $this->mParams = array(); |
241 | | - $this->mCode = ''; |
242 | | - $this->mTokens = array(); |
243 | | - $this->mVars = array(); |
244 | | - $this->mPos = 0; |
245 | | - } |
246 | | - |
247 | | - public function checkSyntax( $filter ) { |
248 | | - try { |
249 | | - $this->parse($filter); |
250 | | - } catch (AFPException $excep) { |
251 | | - return $excep->getMessage(); |
252 | | - } |
253 | | - return true; |
254 | | - } |
255 | | - |
256 | | - public function setVar( $name, $var ) { |
257 | | - $this->mVars[$name] = AFPData::newFromPHPVar( $var ); |
258 | | - } |
259 | | - |
260 | | - public function setVars( $vars ) { |
261 | | - wfProfileIn( __METHOD__ ); |
262 | | - foreach( $vars as $name => $var ) { |
263 | | - $this->setVar( $name, $var ); |
264 | | - } |
265 | | - wfProfileOut( __METHOD__ ); |
266 | | - } |
267 | | - |
268 | | - protected function move( $shift = +1 ) { |
269 | | - $old = $this->mPos; |
270 | | - $this->mPos += $shift; |
271 | | - if( $this->mPos >= 0 && $this->mPos < count( $this->mTokens ) ) { |
272 | | - $this->mCur = $this->mTokens[$this->mPos]; |
273 | | - return true; |
274 | | - } |
275 | | - else { |
276 | | - $this->mPos = $old; |
277 | | - return false; |
278 | | - } |
279 | | - } |
280 | | - |
281 | | - public function parse( $code ) { |
282 | | - wfProfileIn( __METHOD__ ); |
283 | | - $this->mCode = $code; |
284 | | - $this->mTokens = self::parseTokens( $code ); |
285 | | - $this->mPos = 0; |
286 | | - $this->mCur = $this->mTokens[0]; |
287 | | - $result = new AFPData(); |
288 | | - $this->doLevelEntry( $result ); |
289 | | - wfProfileOut( __METHOD__ ); |
290 | | - return $result->toBool(); |
291 | | - } |
292 | | - |
293 | | - public function evaluateExpression( $filter ) { |
294 | | - wfProfileIn( __METHOD__ ); |
295 | | - $this->mCode = $code; |
296 | | - $this->mTokens = self::parseTokens( $code ); |
297 | | - $this->mPos = 0; |
298 | | - $this->mCur = $this->mTokens[0]; |
299 | | - $result = new AFPData(); |
300 | | - $this->doLevelEntry( $result ); |
301 | | - wfProfileOut( __METHOD__ ); |
302 | | - return $result->toString(); |
303 | | - } |
304 | | - |
305 | | - /* Levels */ |
306 | | - |
307 | | - /** Handles unexpected characters after the expression */ |
308 | | - protected function doLevelEntry( &$result ) { |
309 | | - $this->doLevelSet( $result ); |
310 | | - if( $this->mCur->type != AFPToken::TNone ) { |
311 | | - throw new AFPException( "Unexpected {$this->mCur->type} at char {$this->mCur->pos}" ); |
312 | | - } |
313 | | - } |
314 | | - |
315 | | - /** Handles "=" operator */ |
316 | | - protected function doLevelSet( &$result ) { |
317 | | - wfProfileIn( __METHOD__ ); |
318 | | - if( $this->mCur->type == AFPToken::TID ) { |
319 | | - $varname = $this->mCur->value; |
320 | | - $this->move(); |
321 | | - if( $this->mCur->type == AFPToken::TOp && $this->mCur->value == '=' ) { |
322 | | - $this->move(); |
323 | | - $this->doLevelSet( $result ); |
324 | | - $this->mVars[$varname] = $result->dup(); |
325 | | - return; |
326 | | - } |
327 | | - $this->move( -1 ); |
328 | | - } |
329 | | - wfProfileOut( __METHOD__ ); |
330 | | - $this->doLevelBoolOps( $result ); |
331 | | - } |
332 | | - |
333 | | - protected function doLevelBoolOps( &$result ) { |
334 | | - $this->doLevelCompares( $result ); |
335 | | - $ops = array( '&', '|', '^' ); |
336 | | - while( $this->mCur->type == AFPToken::TOp && in_array( $this->mCur->value, $ops ) ) { |
337 | | - $op = $this->mCur->value; |
338 | | - $this->move(); |
339 | | - $r2 = new AFPData(); |
340 | | - $this->doLevelCompares( $r2 ); |
341 | | - wfProfileIn( __METHOD__ ); |
342 | | - $result = AFPData::boolOp( $result, $r2, $op ); |
343 | | - wfProfileOut( __METHOD__ ); |
344 | | - } |
345 | | - } |
346 | | - |
347 | | - protected function doLevelCompares( &$result ) { |
348 | | - $this->doLevelMulRels( $result ); |
349 | | - $ops = array( '==', '===', '!=', '!==', '<', '>', '<=', '>=' ); |
350 | | - while( $this->mCur->type == AFPToken::TOp && in_array( $this->mCur->value, $ops ) ) { |
351 | | - $op = $this->mCur->value; |
352 | | - $this->move(); |
353 | | - $r2 = new AFPData(); |
354 | | - $this->doLevelMulRels( $r2 ); |
355 | | - wfProfileIn( __METHOD__ ); |
356 | | - $result = AFPData::compareOp( $result, $r2, $op ); |
357 | | - wfProfileOut( __METHOD__ ); |
358 | | - } |
359 | | - } |
360 | | - |
361 | | - protected function doLevelMulRels( &$result ) { |
362 | | - $this->doLevelSumRels( $result ); |
363 | | - wfProfileIn( __METHOD__ ); |
364 | | - $ops = array( '*', '/', '%' ); |
365 | | - while( $this->mCur->type == AFPToken::TOp && in_array( $this->mCur->value, $ops ) ) { |
366 | | - $op = $this->mCur->value; |
367 | | - $this->move(); |
368 | | - $r2 = new AFPData(); |
369 | | - $this->doLevelSumRels( $r2 ); |
370 | | - $result = AFPData::mulRel( $result, $r2, $op ); |
371 | | - } |
372 | | - wfProfileOut( __METHOD__ ); |
373 | | - } |
374 | | - |
375 | | - protected function doLevelSumRels( &$result ) { |
376 | | - $this->doLevelPow( $result ); |
377 | | - wfProfileIn( __METHOD__ ); |
378 | | - $ops = array( '+', '-' ); |
379 | | - while( $this->mCur->type == AFPToken::TOp && in_array( $this->mCur->value, $ops ) ) { |
380 | | - $op = $this->mCur->value; |
381 | | - $this->move(); |
382 | | - $r2 = new AFPData(); |
383 | | - $this->doLevelPow( $r2 ); |
384 | | - if( $op == '+' ) |
385 | | - $result = AFPData::sum( $result, $r2 ); |
386 | | - if( $op == '-' ) |
387 | | - $result = AFPData::sub( $result, $r2 ); |
388 | | - } |
389 | | - wfProfileOut( __METHOD__ ); |
390 | | - } |
391 | | - |
392 | | - protected function doLevelPow( &$result ) { |
393 | | - $this->doLevelBoolInvert( $result ); |
394 | | - wfProfileIn( __METHOD__ ); |
395 | | - while( $this->mCur->type == AFPToken::TOp && $this->mCur->value == '**' ) { |
396 | | - $this->move(); |
397 | | - $expanent = new AFPData(); |
398 | | - $this->doLevelBoolInvert( $expanent ); |
399 | | - $result = AFPData::pow( $result, $expanent ); |
400 | | - } |
401 | | - wfProfileOut( __METHOD__ ); |
402 | | - } |
403 | | - |
404 | | - protected function doLevelBoolInvert( &$result ) { |
405 | | - if( $this->mCur->type == AFPToken::TOp && $this->mCur->value == '!' ) { |
406 | | - $this->move(); |
407 | | - $this->doLevelSpecialWords( $result ); |
408 | | - wfProfileIn( __METHOD__ ); |
409 | | - $result = AFPData::boolInvert( $result ); |
410 | | - wfProfileOut( __METHOD__ ); |
411 | | - } else { |
412 | | - $this->doLevelSpecialWords( $result ); |
413 | | - } |
414 | | - } |
415 | | - |
416 | | - protected function doLevelSpecialWords( &$result ) { |
417 | | - $this->doLevelUnarys( $result ); |
418 | | - $specwords = array( 'in', 'like' ); |
419 | | - if( $this->mCur->type == AFPToken::TKeyword && in_array( $this->mCur->value, $specwords ) ) { |
420 | | - $func = 'keyword' . ucfirst( $this->mCur->value ); |
421 | | - $this->move(); |
422 | | - $r2 = new AFPData(); |
423 | | - $this->doLevelUnarys( $r2 ); |
424 | | - wfProfileIn( __METHOD__ ); |
425 | | - wfProfileIn( __METHOD__."-$func" ); |
426 | | - $result = AFPData::$func( $result, $r2 ); |
427 | | - wfProfileOut( __METHOD__."-$func" ); |
428 | | - wfProfileOut( __METHOD__ ); |
429 | | - } |
430 | | - } |
431 | | - |
432 | | - protected function doLevelUnarys( &$result ) { |
433 | | - $op = $this->mCur->value; |
434 | | - if( $this->mCur->type == AFPToken::TOp && ( $op == "+" || $op == "-" ) ) { |
435 | | - $this->move(); |
436 | | - $this->doLevelBraces( $result ); |
437 | | - wfProfileIn( __METHOD__ ); |
438 | | - if( $op == '-' ) { |
439 | | - $result = AFPData::unaryMinus( $result ); |
440 | | - } |
441 | | - wfProfileOut( __METHOD__ ); |
442 | | - } else { |
443 | | - $this->doLevelBraces( $result ); |
444 | | - } |
445 | | - } |
446 | | - |
447 | | - protected function doLevelBraces( &$result ) { |
448 | | - if( $this->mCur->type == AFPToken::TBrace && $this->mCur->value == '(' ) { |
449 | | - $this->move(); |
450 | | - $this->doLevelSet( $result ); |
451 | | - if( !($this->mCur->type == AFPToken::TBrace && $this->mCur->value == ')') ) |
452 | | - throw new AFPException( "Expected ) at char {$this->mCur->pos}" ); |
453 | | - $this->move(); |
454 | | - } else { |
455 | | - $this->doLevelFunction( $result ); |
456 | | - } |
457 | | - } |
458 | | - |
459 | | - protected function doLevelFunction( &$result ) { |
460 | | - if( $this->mCur->type == AFPToken::TID && isset( self::$mFunctions[$this->mCur->value] ) ) { |
461 | | - wfProfileIn( __METHOD__ ); |
462 | | - $func = self::$mFunctions[$this->mCur->value]; |
463 | | - $this->move(); |
464 | | - if( $this->mCur->type != AFPToken::TBrace || $this->mCur->value != '(' ) |
465 | | - throw new AFPEexception( "Expected ( at char {$this->mCur->value}" ); |
466 | | - wfProfileIn( __METHOD__."-loadargs" ); |
467 | | - $args = array(); |
468 | | - if( $this->mCur->type != AFPToken::TBrace || $this->mCur->value != ')' ) |
469 | | - do { |
470 | | - $this->move(); |
471 | | - $r = new AFPData(); |
472 | | - try { |
473 | | - $this->doLevelAtom( $r ); |
474 | | - } catch (AFPException $e) { |
475 | | - $this->move( -1 ); |
476 | | - $this->doLevelSet( $r ); |
477 | | - } |
478 | | - $args[] = $r; |
479 | | - } while( $this->mCur->type == AFPToken::TComma ); |
480 | | - if( $this->mCur->type != AFPToken::TBrace || $this->mCur->value != ')' ) { |
481 | | - throw new AFPException( "Expected ) at char {$this->mCur->pos}" ); |
482 | | - } |
483 | | - wfProfileOut( __METHOD__."-loadargs" ); |
484 | | - |
485 | | - wfProfileIn( __METHOD__."-$func" ); |
486 | | - |
487 | | - $funcHash = md5($func.serialize($args)); |
488 | | - |
489 | | - if (isset(self::$funcCache[$funcHash])) { |
490 | | - $result = self::$funcCache[$funcHash]; |
491 | | - } else { |
492 | | - $result = self::$funcCache[$funcHash] = $this->$func( $args ); |
493 | | - } |
494 | | - |
495 | | - if (count(self::$funcCache) > 1000) { |
496 | | - self::$funcCache = array(); |
497 | | - } |
498 | | - |
499 | | - wfProfileOut( __METHOD__."-$func" ); |
500 | | - |
501 | | - $this->move(); |
502 | | - wfProfileOut( __METHOD__ ); |
503 | | - } else { |
504 | | - $this->doLevelAtom( $result ); |
505 | | - } |
506 | | - } |
507 | | - |
508 | | - protected function doLevelAtom( &$result ) { |
509 | | - wfProfileIn( __METHOD__ ); |
510 | | - $tok = $this->mCur->value; |
511 | | - switch( $this->mCur->type ) { |
512 | | - case AFPToken::TID: |
513 | | - if( isset( $this->mVars[$tok] ) ) { |
514 | | - $result = $this->mVars[$tok]; |
515 | | - } else { |
516 | | - $result = new AFPData(); |
517 | | - } |
518 | | - break; |
519 | | - case AFPToken::TString: |
520 | | - $result = new AFPData( AFPData::DString, $tok ); |
521 | | - break; |
522 | | - case AFPToken::TNumber: |
523 | | - $result = new AFPData( AFPData::DNumber, $tok ); |
524 | | - break; |
525 | | - case AFPToken::TKeyword: |
526 | | - if( $tok == "true" ) |
527 | | - $result = new AFPData( AFPData::DBool, true ); |
528 | | - elseif( $tok == "false" ) |
529 | | - $result = new AFPData( AFPData::DBool, false ); |
530 | | - elseif( $tok == "null" ) |
531 | | - $result = new AFPData(); |
532 | | - else |
533 | | - throw new AFPException( "Unexpected {$this->mCur->type} at char {$this->mCur->pos}" ); |
534 | | - break; |
535 | | - case AFPToken::TBrace: |
536 | | - if( $this->mCur->value == ')' ) |
537 | | - return; // Handled at the entry level |
538 | | - default: |
539 | | - throw new AFPException( "Unexpected {$this->mCur->type} at char {$this->mCur->pos}" ); |
540 | | - } |
541 | | - $this->move(); |
542 | | - wfProfileOut( __METHOD__ ); |
543 | | - } |
544 | | - |
545 | | - /* End of levels */ |
546 | | - |
547 | | - public static function parseTokens( $code ) { |
548 | | - $r = array(); |
549 | | - $len = strlen( $code ); |
550 | | - $hash = md5(trim($code)); |
551 | | - |
552 | | - if (isset(self::$parserCache[$hash])) { |
553 | | - return self::$parserCache[$hash]; |
554 | | - } |
555 | | - |
556 | | - while( $tok = self::nextToken( $code, $len ) ) { |
557 | | - list( $val, $type, $code, $pos ) = $tok; |
558 | | - $r[] = new AFPToken( $type, $val, $pos ); |
559 | | - if( $type == AFPToken::TNone ) |
560 | | - break; |
561 | | - } |
562 | | - return self::$parserCache[$hash] = $r; |
563 | | - } |
564 | | - |
565 | | - protected static function nextToken( $code, $len ) { |
566 | | - $tok = ''; |
567 | | - if( strlen( $code ) == 0 ) return array( '', AFPToken::TNone, $code, $len ); |
568 | | - while( ctype_space( $code[0] ) ) |
569 | | - $code = substr( $code, 1 ); |
570 | | - $pos = $len - strlen( $code ); |
571 | | - if( strlen( $code ) == 0 ) return array( '', AFPToken::TNone, $code, $pos ); |
572 | | - if( $code[0] == ',' ) |
573 | | - return array( ',', AFPToken::TComma, substr( $code, 1 ), $pos ); |
574 | | - if( $code[0] == '(' or $code[0] == ')' ) |
575 | | - return array( $code[0], AFPToken::TBrace, substr( $code, 1 ), $pos ); |
576 | | - if( $code[0] == '"' || $code[0] == "'" ) { |
577 | | - $type = $code[0]; |
578 | | - $code = substr( $code, 1 ); |
579 | | - while( strlen( $code ) != 0 ) { |
580 | | - if( $code[0] == $type ) { |
581 | | - return array( $tok, AFPToken::TString, substr( $code, 1 ), $pos ); |
582 | | - } |
583 | | - if( $code[0] == '\\' ) { |
584 | | - if( $code[1] == '\\' ) |
585 | | - $tok .= '\\'; |
586 | | - elseif( $code[1] == $type ) |
587 | | - $tok .= $type; |
588 | | - elseif( $code[1] == 'n' ) |
589 | | - $tok .= "\n"; |
590 | | - elseif( $code[1] == 'r' ) |
591 | | - $tok .= "\r"; |
592 | | - elseif( $code[1] == 't' ) |
593 | | - $tok .= "\t"; |
594 | | - else |
595 | | - $tok .= $code[1]; |
596 | | - $code = substr( $code, 2 ); |
597 | | - } else { |
598 | | - $tok .= $code[0]; |
599 | | - $code = substr( $code, 1 ); |
600 | | - } |
601 | | - } |
602 | | - throw new AFPException( "Unclosed string begining at char $pos" ); |
603 | | - } |
604 | | - if( ctype_punct( $code[0] ) ) { |
605 | | - $tok .= $code[0]; |
606 | | - $code = substr( $code, 1 ); |
607 | | - while( strlen( $code ) != 0 && ctype_punct( $code[0] ) ) { |
608 | | - $tok .= $code[0]; |
609 | | - $code = substr( $code, 1 ); |
610 | | - } |
611 | | - if( !in_array( $tok, self::$mOps ) ) |
612 | | - throw new AFPException( "Invalid operator: {$tok} (at char $pos)" ); |
613 | | - return array( $tok, AFPToken::TOp, $code, $pos ); |
614 | | - } |
615 | | - if( ctype_digit( $code[0] ) ) { |
616 | | - $tok .= $code[0]; |
617 | | - $code = substr( $code, 1 ); |
618 | | - while( strlen( $code ) != 0 && self::isDigitOrDot( $code[0] ) ) { |
619 | | - $tok .= $code[0]; |
620 | | - $code = substr( $code, 1 ); |
621 | | - } |
622 | | - return array( in_string( '.', $tok ) ? doubleval( $tok ) : intval( $tok ), AFPToken::TNumber, $code, $pos ); |
623 | | - } |
624 | | - if( self::isValidIdSymbol( $code[0] ) ) { |
625 | | - while( strlen( $code ) != 0 && self::isValidIdSymbol( $code[0] ) ) { |
626 | | - $tok .= $code[0]; |
627 | | - $code = substr( $code, 1 ); |
628 | | - } |
629 | | - $type = in_array( $tok, self::$mKeywords ) ? AFPToken::TKeyword : AFPToken::TID; |
630 | | - return array( $tok, $type, $code, $pos ); |
631 | | - } |
632 | | - throw new AFPException( "Unrecognized token \"{$code[0]}\" at char $pos" ); |
633 | | - } |
634 | | - |
635 | | - protected static function isDigitOrDot( $chr ) { |
636 | | - return ctype_digit( $chr ) || $chr == '.'; |
637 | | - } |
638 | | - |
639 | | - protected static function isValidIdSymbol( $chr ) { |
640 | | - return ctype_alnum( $chr ) || $chr == '_'; |
641 | | - } |
642 | | - |
643 | | - //Built-in functions |
644 | | - protected function funcLc( $args ) { |
645 | | - global $wgContLang; |
646 | | - if( count( $args ) < 1 ) |
647 | | - throw new AFPExpection( "No params passed to lc()" ); |
648 | | - $s = $args[0]->toString(); |
649 | | - return new AFPData( AFPData::DString, $wgContLang->lc( $s ) ); |
650 | | - } |
651 | | - |
652 | | - protected function funcLen( $args ) { |
653 | | - if( count( $args ) < 1 ) |
654 | | - throw new AFPExpection( "No params passed to len()" ); |
655 | | - $s = $args[0]->toString(); |
656 | | - return new AFPData( AFPData::DNumber, mb_strlen( $s, 'utf-8' ) ); |
657 | | - } |
658 | | - |
659 | | - protected function funcNorm( $args ) { |
660 | | - if( count( $args ) < 1 ) |
661 | | - throw new AFPExpection( "No params passed to norm()" ); |
662 | | - $s = $args[0]->toString(); |
663 | | - return new AFPData( AFPData::DString, AbuseFilter::normalise( $s ) ); |
664 | | - } |
665 | | - |
666 | | - protected function funcSimpleNorm( $args ) { |
667 | | - if( count( $args ) < 1 ) |
668 | | - throw new AFPExpection( "No params passed to simplenorm()" ); |
669 | | - $s = $args[0]->toString(); |
670 | | - |
671 | | - $s = preg_replace( '/[\d\W]+/', '', $s ); |
672 | | - $s = strtolower( $value ); |
673 | | - return new AFPData( AFPData::DString, $s ); |
674 | | - } |
675 | | - |
676 | | - protected function funcSpecialRatio( $args ) { |
677 | | - if( count( $args ) < 1 ) |
678 | | - throw new AFPExpection( "No params passed to simplenorm()" ); |
679 | | - $s = $args[0]->toString(); |
680 | | - |
681 | | - if (!strlen($s)) { |
682 | | - return new AFPData( AFPData::DNumber, 0 ); |
683 | | - } |
684 | | - |
685 | | - $specialsonly = preg_replace('/\w/', '', $s ); |
686 | | - $val = (strlen($specialsonly) / strlen($s)); |
687 | | - |
688 | | - return new AFPData( AFPData::DNumber, $val ); |
689 | | - } |
690 | | -} |
Index: trunk/extensions/AbuseFilter/AbuseFilter.php |
— | — | @@ -28,7 +28,6 @@ |
29 | 29 | $wgExtensionAliasesFiles['AbuseFilter'] = "$dir/AbuseFilter.alias.php"; |
30 | 30 | |
31 | 31 | $wgAutoloadClasses[ 'AbuseFilter' ] = "$dir/AbuseFilter.class.php"; |
32 | | -$wgAutoloadClasses[ 'AbuseFilterParser' ] = "$dir/AbuseFilter.parser.php"; |
33 | 32 | $wgAutoloadClasses[ 'AbuseFilterParserNative' ] = "$dir/AbuseFilter.nativeparser.php"; |
34 | 33 | $wgAutoloadClasses[ 'AbuseFilterHooks' ] = "$dir/AbuseFilter.hooks.php"; |
35 | 34 | $wgAutoloadClasses['SpecialAbuseLog'] = "$dir/SpecialAbuseLog.php"; |