[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/languages/utils/ -> CLDRPluralRuleEvaluator.php (source)

   1  <?php
   2  
   3  /**
   4   * Parse and evaluate a plural rule.
   5   *
   6   * UTS #35 Revision 33
   7   * http://www.unicode.org/reports/tr35/tr35-33/tr35-numbers.html#Language_Plural_Rules
   8   *
   9   * @author Niklas Laxström, Tim Starling
  10   *
  11   * @copyright Copyright © 2010-2012, Niklas Laxström
  12   * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0
  13   * or later
  14   *
  15   * This program is free software; you can redistribute it and/or modify
  16   * it under the terms of the GNU General Public License as published by
  17   * the Free Software Foundation; either version 2 of the License, or
  18   * (at your option) any later version.
  19   *
  20   * This program is distributed in the hope that it will be useful,
  21   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  23   * GNU General Public License for more details.
  24   *
  25   * You should have received a copy of the GNU General Public License along
  26   * with this program; if not, write to the Free Software Foundation, Inc.,
  27   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  28   * http://www.gnu.org/copyleft/gpl.html
  29   *
  30   *
  31   * @file
  32   * @since 1.20
  33   */
  34  class CLDRPluralRuleEvaluator {
  35      /**
  36       * Evaluate a number against a set of plural rules. If a rule passes,
  37       * return the index of plural rule.
  38       *
  39       * @param int $number The number to be evaluated against the rules
  40       * @param array $rules The associative array of plural rules in pluralform => rule format.
  41       * @return int The index of the plural form which passed the evaluation
  42       */
  43  	public static function evaluate( $number, array $rules ) {
  44          $rules = self::compile( $rules );
  45  
  46          return self::evaluateCompiled( $number, $rules );
  47      }
  48  
  49      /**
  50       * Convert a set of rules to a compiled form which is optimised for
  51       * fast evaluation. The result will be an array of strings, and may be cached.
  52       *
  53       * @param array $rules The rules to compile
  54       * @return array An array of compile rules.
  55       */
  56  	public static function compile( array $rules ) {
  57          // We can't use array_map() for this because it generates a warning if
  58          // there is an exception.
  59          foreach ( $rules as &$rule ) {
  60              $rule = CLDRPluralRuleConverter::convert( $rule );
  61          }
  62  
  63          return $rules;
  64      }
  65  
  66      /**
  67       * Evaluate a compiled set of rules returned by compile(). Do not allow
  68       * the user to edit the compiled form, or else PHP errors may result.
  69       *
  70       * @param string $number The number to be evaluated against the rules, in English, or it
  71       *   may be a type convertible to string.
  72       * @param array $rules The associative array of plural rules in pluralform => rule format.
  73       * @return int The index of the plural form which passed the evaluation
  74       */
  75  	public static function evaluateCompiled( $number, array $rules ) {
  76          // Calculate the values of the operand symbols
  77          $number = strval( $number );
  78          if ( !preg_match( '/^ -? ( ([0-9]+) (?: \. ([0-9]+) )? )$/x', $number, $m ) ) {
  79              wfDebug( __METHOD__ . ": invalid number input, returning 'other'\n" );
  80  
  81              return count( $rules );
  82          }
  83          if ( !isset( $m[3] ) ) {
  84              $operandSymbols = array(
  85                  'n' => intval( $m[1] ),
  86                  'i' => intval( $m[1] ),
  87                  'v' => 0,
  88                  'w' => 0,
  89                  'f' => 0,
  90                  't' => 0
  91              );
  92          } else {
  93              $absValStr = $m[1];
  94              $intStr = $m[2];
  95              $fracStr = $m[3];
  96              $operandSymbols = array(
  97                  'n' => floatval( $absValStr ),
  98                  'i' => intval( $intStr ),
  99                  'v' => strlen( $fracStr ),
 100                  'w' => strlen( rtrim( $fracStr, '0' ) ),
 101                  'f' => intval( $fracStr ),
 102                  't' => intval( rtrim( $fracStr, '0' ) ),
 103              );
 104          }
 105  
 106          // The compiled form is RPN, with tokens strictly delimited by
 107          // spaces, so this is a simple RPN evaluator.
 108          foreach ( $rules as $i => $rule ) {
 109              $stack = array();
 110              $zero = ord( '0' );
 111              $nine = ord( '9' );
 112              foreach ( StringUtils::explode( ' ', $rule ) as $token ) {
 113                  $ord = ord( $token );
 114                  if ( isset( $operandSymbols[$token] ) ) {
 115                      $stack[] = $operandSymbols[$token];
 116                  } elseif ( $ord >= $zero && $ord <= $nine ) {
 117                      $stack[] = intval( $token );
 118                  } else {
 119                      $right = array_pop( $stack );
 120                      $left = array_pop( $stack );
 121                      $result = self::doOperation( $token, $left, $right );
 122                      $stack[] = $result;
 123                  }
 124              }
 125              if ( $stack[0] ) {
 126                  return $i;
 127              }
 128          }
 129          // None of the provided rules match. The number belongs to category
 130          // 'other', which comes last.
 131          return count( $rules );
 132      }
 133  
 134      /**
 135       * Do a single operation
 136       *
 137       * @param string $token The token string
 138       * @param mixed $left The left operand. If it is an object, its state may be destroyed.
 139       * @param mixed $right The right operand
 140       * @throws CLDRPluralRuleError
 141       * @return mixed The operation result
 142       */
 143  	private static function doOperation( $token, $left, $right ) {
 144          if ( in_array( $token, array( 'in', 'not-in', 'within', 'not-within' ) ) ) {
 145              if ( !( $right instanceof CLDRPluralRuleEvaluatorRange ) ) {
 146                  $right = new CLDRPluralRuleEvaluatorRange( $right );
 147              }
 148          }
 149          switch ( $token ) {
 150              case 'or':
 151                  return $left || $right;
 152              case 'and':
 153                  return $left && $right;
 154              case 'is':
 155                  return $left == $right;
 156              case 'is-not':
 157                  return $left != $right;
 158              case 'in':
 159                  return $right->isNumberIn( $left );
 160              case 'not-in':
 161                  return !$right->isNumberIn( $left );
 162              case 'within':
 163                  return $right->isNumberWithin( $left );
 164              case 'not-within':
 165                  return !$right->isNumberWithin( $left );
 166              case 'mod':
 167                  if ( is_int( $left ) ) {
 168                      return (int)fmod( $left, $right );
 169                  }
 170  
 171                  return fmod( $left, $right );
 172              case ',':
 173                  if ( $left instanceof CLDRPluralRuleEvaluatorRange ) {
 174                      $range = $left;
 175                  } else {
 176                      $range = new CLDRPluralRuleEvaluatorRange( $left );
 177                  }
 178                  $range->add( $right );
 179  
 180                  return $range;
 181              case '..':
 182                  return new CLDRPluralRuleEvaluatorRange( $left, $right );
 183              default:
 184                  throw new CLDRPluralRuleError( "Invalid RPN token" );
 185          }
 186      }
 187  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1