sfChoiceFormat.class.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. <?php
  2. /**
  3. * sfChoiceFormat class file.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the BSD License.
  7. *
  8. * Copyright(c) 2004 by Qiang Xue. All rights reserved.
  9. *
  10. * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
  11. * The latest version of PRADO can be obtained from:
  12. * {@link http://prado.sourceforge.net/}
  13. *
  14. * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
  15. * @version $Id: sfChoiceFormat.class.php 9128 2008-05-21 00:58:19Z Carl.Vondrick $
  16. * @package symfony
  17. * @subpackage i18n
  18. */
  19. /**
  20. * sfChoiceFormat class.
  21. *
  22. * sfChoiceFormat converts between ranges of numeric values and string
  23. * names for those ranges.
  24. *
  25. * A sfChoiceFormat splits the real number line -Inf to +Inf into two or
  26. * more contiguous ranges. Each range is mapped to a string.
  27. * sfChoiceFormat is generally used in a MessageFormat for displaying
  28. * grammatically correct plurals such as "There are 2 files."
  29. *
  30. * <code>
  31. * $string = '[0] are no files |[1] is one file |(1,Inf] are {number} files';
  32. *
  33. * $formatter = new sfMessageFormat(...); //init for a source
  34. * $translated = $formatter->format($string);
  35. *
  36. * $choice = new sfChoiceFormat();
  37. * echo $choice->format($translated, 0); //shows "are no files"
  38. * </code>
  39. *
  40. * The message/string choices are separated by the pipe "|" followed
  41. * by a set notation of the form
  42. * # <t>[1,2]</t> -- accepts values between 1 and 2, inclusive.
  43. * # <t>(1,2)</t> -- accepts values between 1 and 2, excluding 1 and 2.
  44. * # <t>{1,2,3,4}</t> -- only values defined in the set are accepted.
  45. * # <t>[-Inf,0)</t> -- accepts value greater or equal to negative infinity
  46. * and strictly less than 0
  47. * Any non-empty combinations of the delimiters of square and round brackets
  48. * are acceptable.
  49. *
  50. * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
  51. * @version v1.0, last update on Fri Dec 24 20:46:16 EST 2004
  52. * @package symfony
  53. * @subpackage i18n
  54. */
  55. class sfChoiceFormat
  56. {
  57. /**
  58. * The pattern to validate a set notation
  59. */
  60. protected $validate = '/[\(\[\{]|[-Inf\d:\s]+|,|[\+Inf\d\s:\?\-=!><%\|&\(\)]+|[\)\]\}]/ms';
  61. /**
  62. * The pattern to parse the formatting string.
  63. */
  64. protected $parse = '/\s*\|?([\(\[\{]([-Inf\d:\s]+,?[\+Inf\d\s:\?\-=!><%\|&\(\)]*)+[\)\]\}])\s*/';
  65. /**
  66. * The value for positive infinity.
  67. */
  68. protected $inf;
  69. /**
  70. * Constructor.
  71. */
  72. public function __construct()
  73. {
  74. $this->inf = -log(0);
  75. }
  76. /**
  77. * Determines if the given number belongs to a given set
  78. *
  79. * @param $number float the number to test.
  80. * @param $set string the set, in set notation.
  81. * @return boolean true if number is in the set, false otherwise.
  82. */
  83. public function isValid($number, $set)
  84. {
  85. $n = preg_match_all($this->validate, $set, $matches, PREG_SET_ORDER);
  86. if ($n < 3)
  87. {
  88. throw new sfException(sprintf('Invalid set "%s".', $set));
  89. }
  90. if (preg_match('/\{\s*n:([^\}]+)\}/', $set, $def))
  91. {
  92. return $this->isValidSetNotation($number, $def[1]);
  93. }
  94. $leftBracket = $matches[0][0];
  95. $rightBracket = $matches[$n - 1][0];
  96. $i = 0;
  97. $elements = array();
  98. foreach ($matches as $match)
  99. {
  100. $string = $match[0];
  101. if ($i != 0 && $i != $n - 1 && $string !== ',')
  102. {
  103. if ($string == '-Inf')
  104. {
  105. $elements[] = -1 * $this->inf;
  106. }
  107. else if ($string == '+Inf' || $string == 'Inf')
  108. {
  109. $elements[] = $this->inf;
  110. }
  111. else
  112. {
  113. $elements[] = floatval($string);
  114. }
  115. }
  116. $i++;
  117. }
  118. $total = count($elements);
  119. $number = floatval($number);
  120. if ($leftBracket == '{' && $rightBracket == '}')
  121. {
  122. return in_array($number, $elements);
  123. }
  124. $left = false;
  125. if ($leftBracket == '[')
  126. {
  127. $left = $number >= $elements[0];
  128. }
  129. else if ($leftBracket == '(')
  130. {
  131. $left = $number > $elements[0];
  132. }
  133. $right = false;
  134. if ($rightBracket == ']')
  135. {
  136. $right = $number <= $elements[$total - 1];
  137. }
  138. else if ($rightBracket == ')')
  139. {
  140. $right = $number < $elements[$total - 1];
  141. }
  142. if ($left && $right)
  143. {
  144. return true;
  145. }
  146. return false;
  147. }
  148. protected function isValidSetNotation($number, $set)
  149. {
  150. $str = '$result = '.str_replace('n', '$number', $set).';';
  151. try
  152. {
  153. eval($str);
  154. return $result;
  155. }
  156. catch (Exception $e)
  157. {
  158. return false;
  159. }
  160. }
  161. /**
  162. * Parses a choice string and get a list of sets and a list of strings corresponding to the sets.
  163. *
  164. * @param $string string the string containing the choices
  165. * @return array array($sets, $strings)
  166. */
  167. public function parse($string)
  168. {
  169. $n = preg_match_all($this->parse, $string, $matches, PREG_OFFSET_CAPTURE);
  170. $sets = array();
  171. foreach ($matches[1] as $match)
  172. {
  173. $sets[] = $match[0];
  174. }
  175. $offset = $matches[0];
  176. $strings = array();
  177. for ($i = 0; $i < $n; $i++)
  178. {
  179. $len = strlen($offset[$i][0]);
  180. $begin = $i == 0 ? $len : $offset[$i][1] + $len;
  181. $end = $i == $n - 1 ? strlen($string) : $offset[$i + 1][1];
  182. $strings[] = substr($string, $begin, $end - $begin);
  183. }
  184. return array($sets, $strings);
  185. }
  186. /**
  187. * For the choice string, and a number, find and return the string that satisfied the set within the choices.
  188. *
  189. * @param string $string the choices string.
  190. * @param float $number the number to test.
  191. * @return string the choosen string.
  192. */
  193. public function format($string, $number)
  194. {
  195. list($sets, $strings) = $this->parse($string);
  196. $total = count($sets);
  197. for ($i = 0; $i < $total; $i++)
  198. {
  199. if ($this->isValid($number, $sets[$i]))
  200. {
  201. return $strings[$i];
  202. }
  203. }
  204. return false;
  205. }
  206. }