1. sfChoiceFormat.class.php
  2. /** * sfChoiceFormat class. * * sfChoiceFormat converts between ranges of numeric values and string * names for those ranges. * * A sfChoiceFormat splits the real number line -Inf to +Inf into two or * more contiguous ranges. Each range is mapped to a string. * sfChoiceFormat is generally used in a MessageFormat for displaying * grammatically correct plurals such as "There are 2 files." * * * $string = '[0] are no files |[1] is one file |(1,Inf] are {number} files'; * * $formatter = new sfMessageFormat(...); //init for a source * $translated = $formatter->format($string); * * $choice = new sfChoiceFormat(); * echo $choice->format($translated, 0); //shows "are no files" * * * The message/string choices are separated by the pipe "|" followed * by a set notation of the form * # [1,2] -- accepts values between 1 and 2, inclusive. * # (1,2) -- accepts values between 1 and 2, excluding 1 and 2. * # {1,2,3,4} -- only values defined in the set are accepted. * # [-Inf,0) -- accepts value greater or equal to negative infinity * and strictly less than 0 * Any non-empty combinations of the delimiters of square and round brackets * are acceptable. * * @author Xiang Wei Zhuo * @version v1.0, last update on Fri Dec 24 20:46:16 EST 2004 * @package symfony * @subpackage i18n */
  3. class sfChoiceFormat
  4. {
  5. /**
  6. * The pattern to validate a set notation
  7. */
  8. protected $validate = '/[\(\[\{]|[-Inf\d:\s]+|,|[\+Inf\d\s:\?\-=!><%\|&\(\)]+|[\)\]\}]/ms';
  9. /**
  10. * The pattern to parse the formatting string.
  11. */
  12. protected $parse = '/\s*\|?([\(\[\{]([-Inf\d:\s]+,?[\+Inf\d\s:\?\-=!><%\|&\(\)]*)+[\)\]\}])\s*/';
  13. /**
  14. * The value for positive infinity.
  15. */
  16. protected $inf;
  17. /**
  18. * Constructor.
  19. */
  20. public function __construct()
  21. {
  22. $this->inf = -log(0);
  23. }
  24. /**
  25. * Determines if the given number belongs to a given set
  26. *
  27. * @param float $number the number to test.
  28. * @param string $set the set, in set notation.
  29. * @return boolean true if number is in the set, false otherwise.
  30. */
  31. public function isValid($number, $set)
  32. {
  33. $n = preg_match_all($this->validate, $set, $matches, PREG_SET_ORDER);
  34. if ($n < 3)
  35. {
  36. throw new sfException(sprintf('Invalid set "%s".', $set));
  37. }
  38. if (preg_match('/\{\s*n:([^\}]+)\}/', $set, $def))
  39. {
  40. return $this->isValidSetNotation($number, $def[1]);
  41. }
  42. $leftBracket = $matches[0][0];
  43. $rightBracket = $matches[$n - 1][0];
  44. $i = 0;
  45. $elements = array();
  46. foreach ($matches as $match)
  47. {
  48. $string = $match[0];
  49. if ($i != 0 && $i != $n - 1 && $string !== ',')
  50. {
  51. if ($string == '-Inf')
  52. {
  53. $elements[] = -1 * $this->inf;
  54. }
  55. else if ($string == '+Inf' || $string == 'Inf')
  56. {
  57. $elements[] = $this->inf;
  58. }
  59. else
  60. {
  61. $elements[] = floatval($string);
  62. }
  63. }
  64. $i++;
  65. }
  66. $total = count($elements);
  67. $number = floatval($number);
  68. if ($leftBracket == '{' && $rightBracket == '}')
  69. {
  70. return in_array($number, $elements);
  71. }
  72. $left = false;
  73. if ($leftBracket == '[')
  74. {
  75. $left = $number >= $elements[0];
  76. }
  77. else if ($leftBracket == '(')
  78. {
  79. $left = $number > $elements[0];
  80. }
  81. $right = false;
  82. if ($rightBracket == ']')
  83. {
  84. $right = $number <= $elements[$total - 1];
  85. }
  86. else if ($rightBracket == ')')
  87. {
  88. $right = $number < $elements[$total - 1];
  89. }
  90. if ($left && $right)
  91. {
  92. return true;
  93. }
  94. return false;
  95. }
  96. protected function isValidSetNotation($number, $set)
  97. {
  98. $str = '$result = '.str_replace('n', '$number', $set).';';
  99. try
  100. {
  101. eval($str);
  102. return $result;
  103. }
  104. catch (Exception $e)
  105. {
  106. return false;
  107. }
  108. }
  109. /**
  110. * Parses a choice string and get a list of sets and a list of strings corresponding to the sets.
  111. *
  112. * @param string $string the string containing the choices
  113. * @return array array($sets, $strings)
  114. */
  115. public function parse($string)
  116. {
  117. $n = preg_match_all($this->parse, $string, $matches, PREG_OFFSET_CAPTURE);
  118. $sets = array();
  119. foreach ($matches[1] as $match)
  120. {
  121. $sets[] = $match[0];
  122. }
  123. $offset = $matches[0];
  124. $strings = array();
  125. for ($i = 0; $i < $n; $i++)
  126. {
  127. $len = strlen($offset[$i][0]);
  128. $begin = $i == 0 ? $len : $offset[$i][1] + $len;
  129. $end = $i == $n - 1 ? strlen($string) : $offset[$i + 1][1];
  130. $strings[] = substr($string, $begin, $end - $begin);
  131. }
  132. return array($sets, $strings);
  133. }
  134. /**
  135. * For the choice string, and a number, find and return the string that satisfied the set within the choices.
  136. *
  137. * @param string $string the choices string.
  138. * @param float $number the number to test.
  139. * @return string the choosen string.
  140. */
  141. public function format($string, $number)
  142. {
  143. list($sets, $strings) = $this->parse($string);
  144. $total = count($sets);
  145. for ($i = 0; $i < $total; $i++)
  146. {
  147. if ($this->isValid($number, $sets[$i]))
  148. {
  149. return $strings[$i];
  150. }
  151. }
  152. return false;
  153. }
  154. }

Debug toolbar