Joomla Platform  13.1
Documentation des API du framework Joomla Platform
 Tout Classes Espaces de nommage Fichiers Fonctions Variables Pages
inflector.php
Aller à la documentation de ce fichier.
1 <?php
2 /**
3  * @package Joomla.Platform
4  * @subpackage String
5  *
6  * @copyright Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved.
7  * @license GNU General Public License version 2 or later; see LICENSE
8  */
9 
10 defined('JPATH_PLATFORM') or die;
11 
12 /**
13  * Joomla Platform String Inflector Class
14  *
15  * The Inflector transforms words
16  *
17  * @package Joomla.Platform
18  * @subpackage String
19  * @since 12.1
20  */
22 {
23  /**
24  * The singleton instance.
25  *
26  * @var JStringInflector
27  * @since 12.1
28  */
29  private static $_instance;
30 
31  /**
32  * The inflector rules for singularisation, pluralisation and countability.
33  *
34  * @var array
35  * @since 12.1
36  */
37  private $_rules = array(
38  'singular' => array(
39  '/(matr)ices$/i' => '\1ix',
40  '/(vert|ind)ices$/i' => '\1ex',
41  '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
42  '/([ftw]ax)es/i' => '\1',
43  '/(cris|ax|test)es$/i' => '\1is',
44  '/(shoe|slave)s$/i' => '\1',
45  '/(o)es$/i' => '\1',
46  '/([^aeiouy]|qu)ies$/i' => '\1y',
47  '/$1ses$/i' => '\s',
48  '/ses$/i' => '\s',
49  '/eaus$/' => 'eau',
50  '/^(.*us)$/' => '\\1',
51  '/s$/i' => '',
52  ),
53  'plural' => array(
54  '/([m|l])ouse$/i' => '\1ice',
55  '/(matr|vert|ind)(ix|ex)$/i' => '\1ices',
56  '/(x|ch|ss|sh)$/i' => '\1es',
57  '/([^aeiouy]|qu)y$/i' => '\1ies',
58  '/([^aeiouy]|qu)ies$/i' => '\1y',
59  '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
60  '/sis$/i' => 'ses',
61  '/([ti])um$/i' => '\1a',
62  '/(buffal|tomat)o$/i' => '\1\2oes',
63  '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i',
64  '/us$/i' => 'uses',
65  '/(ax|cris|test)is$/i' => '\1es',
66  '/s$/i' => 's',
67  '/$/' => 's',
68  ),
69  'countable' => array(
70  'id',
71  'hits',
72  'clicks',
73  ),
74  );
75 
76  /**
77  * Cached inflections.
78  *
79  * The array is in the form [singular => plural]
80  *
81  * @var array
82  * @since 12.1
83  */
84  private $_cache = array();
85 
86  /**
87  * Protected constructor.
88  *
89  * @since 12.1
90  */
91  protected function __construct()
92  {
93  // Pre=populate the irregual singular/plural.
94  $this
95  ->addWord('deer')
96  ->addWord('moose')
97  ->addWord('sheep')
98  ->addWord('bison')
99  ->addWord('salmon')
100  ->addWord('pike')
101  ->addWord('trout')
102  ->addWord('fish')
103  ->addWord('swine')
104 
105  ->addWord('alias', 'aliases')
106  ->addWord('bus', 'buses')
107  ->addWord('foot', 'feet')
108  ->addWord('goose', 'geese')
109  ->addWord('hive', 'hives')
110  ->addWord('louse', 'lice')
111  ->addWord('man', 'men')
112  ->addWord('mouse', 'mice')
113  ->addWord('ox', 'oxen')
114  ->addWord('quiz', 'quizes')
115  ->addWord('status', 'statuses')
116  ->addWord('tooth', 'teeth')
117  ->addWord('woman', 'women');
118  }
119 
120  /**
121  * Adds inflection regex rules to the inflector.
122  *
123  * @param mixed $data A string or an array of strings or regex rules to add.
124  * @param string $ruleType The rule type: singular | plural | countable
125  *
126  * @return void
127  *
128  * @since 12.1
129  * @throws InvalidArgumentException
130  */
131  private function _addRule($data, $ruleType)
132  {
133  if (is_string($data))
134  {
135  $data = array($data);
136  }
137  elseif (!is_array($data))
138  {
139  // Do not translate.
140  throw new InvalidArgumentException('Invalid inflector rule data.');
141  }
142 
143  foreach ($data as $rule)
144  {
145  // Ensure a string is pushed.
146  array_push($this->_rules[$ruleType], (string) $rule);
147  }
148  }
149 
150  /**
151  * Gets an inflected word from the cache where the singular form is supplied.
152  *
153  * @param string $singular A singular form of a word.
154  *
155  * @return mixed The cached inflection or false if none found.
156  *
157  * @since 12.1
158  */
159  private function _getCachedPlural($singular)
160  {
161  $singular = JString::strtolower($singular);
162 
163  // Check if the word is in cache.
164  if (isset($this->_cache[$singular]))
165  {
166  return $this->_cache[$singular];
167  }
168 
169  return false;
170  }
171 
172  /**
173  * Gets an inflected word from the cache where the plural form is supplied.
174  *
175  * @param string $plural A plural form of a word.
176  *
177  * @return mixed The cached inflection or false if none found.
178  *
179  * @since 12.1
180  */
181  private function _getCachedSingular($plural)
182  {
183  $plural = JString::strtolower($plural);
184 
185  return array_search($plural, $this->_cache);
186  }
187 
188  /**
189  * Execute a regex from rules.
190  *
191  * The 'plural' rule type expects a singular word.
192  * The 'singular' rule type expects a plural word.
193  *
194  * @param string $word The string input.
195  * @param string $ruleType String (eg, singular|plural)
196  *
197  * @return mixed An inflected string, or false if no rule could be applied.
198  *
199  * @since 12.1
200  */
201  private function _matchRegexRule($word, $ruleType)
202  {
203  // Cycle through the regex rules.
204  foreach ($this->_rules[$ruleType] as $regex => $replacement)
205  {
206  $matches = 0;
207  $matchedWord = preg_replace($regex, $replacement, $word, -1, $matches);
208 
209  if ($matches > 0)
210  {
211  return $matchedWord;
212  }
213  }
214 
215  return false;
216  }
217 
218  /**
219  * Sets an inflected word in the cache.
220  *
221  * @param string $singular The singular form of the word.
222  * @param string $plural The plural form of the word. If omitted, it is assumed the singular and plural are identical.
223  *
224  * @return void
225  *
226  * @since 12.1
227  */
228  private function _setCache($singular, $plural = null)
229  {
230  $singular = JString::strtolower($singular);
231 
232  if ($plural === null)
233  {
234  $plural = $singular;
235  }
236  else
237  {
238  $plural = JString::strtolower($plural);
239  }
240 
241  $this->_cache[$singular] = $plural;
242  }
243 
244  /**
245  * Adds a countable word.
246  *
247  * @param mixed $data A string or an array of strings to add.
248  *
249  * @return JStringInflector Returns this object to support chaining.
250  *
251  * @since 12.1
252  */
253  public function addCountableRule($data)
254  {
255  $this->_addRule($data, 'countable');
256 
257  return $this;
258  }
259 
260  /**
261  * Adds a specific singular-plural pair for a word.
262  *
263  * @param string $singular The singular form of the word.
264  * @param string $plural The plural form of the word. If omitted, it is assumed the singular and plural are identical.
265  *
266  * @return JStringInflector Returns this object to support chaining.
267  *
268  * @since 12.1
269  */
270  public function addWord($singular, $plural =null)
271  {
272  $this->_setCache($singular, $plural);
273 
274  return $this;
275  }
276 
277  /**
278  * Adds a pluralisation rule.
279  *
280  * @param mixed $data A string or an array of regex rules to add.
281  *
282  * @return JStringInflector Returns this object to support chaining.
283  *
284  * @since 12.1
285  */
286  public function addPluraliseRule($data)
287  {
288  $this->_addRule($data, 'plural');
289 
290  return $this;
291  }
292 
293  /**
294  * Adds a singularisation rule.
295  *
296  * @param mixed $data A string or an array of regex rules to add.
297  *
298  * @return JStringInflector Returns this object to support chaining.
299  *
300  * @since 12.1
301  */
302  public function addSingulariseRule($data)
303  {
304  $this->_addRule($data, 'singular');
305 
306  return $this;
307  }
308 
309  /**
310  * Gets an instance of the JStringInflector singleton.
311  *
312  * @param boolean $new If true (default is false), returns a new instance regardless if one exists.
313  * This argument is mainly used for testing.
314  *
315  * @return JStringInflector
316  *
317  * @since 12.1
318  */
319  public static function getInstance($new = false)
320  {
321  if ($new)
322  {
323  return new static;
324  }
325  elseif (!is_object(self::$_instance))
326  {
327  self::$_instance = new static;
328  }
329 
330  return self::$_instance;
331  }
332 
333  /**
334  * Checks if a word is countable.
335  *
336  * @param string $word The string input.
337  *
338  * @return boolean True if word is countable, false otherwise.
339  *
340  * @since 12.1
341  */
342  public function isCountable($word)
343  {
344  return (boolean) in_array($word, $this->_rules['countable']);
345  }
346 
347  /**
348  * Checks if a word is in a plural form.
349  *
350  * @param string $word The string input.
351  *
352  * @return boolean True if word is plural, false if not.
353  *
354  * @since 12.1
355  */
356  public function isPlural($word)
357  {
358  // Try the cache for an known inflection.
359  $inflection = $this->_getCachedSingular($word);
360 
361  if ($inflection !== false)
362  {
363  return true;
364  }
365 
366  // Compute the inflection to cache the values, and compare.
367  return $this->toPlural($this->toSingular($word)) == $word;
368  }
369 
370  /**
371  * Checks if a word is in a singular form.
372  *
373  * @param string $word The string input.
374  *
375  * @return boolean True if word is singular, false if not.
376  *
377  * @since 12.1
378  */
379  public function isSingular($word)
380  {
381  // Try the cache for an known inflection.
382  $inflection = $this->_getCachedPlural($word);
383 
384  if ($inflection !== false)
385  {
386  return true;
387  }
388 
389  // Compute the inflection to cache the values, and compare.
390  return $this->toSingular($this->toPlural($word)) == $word;
391  }
392 
393  /**
394  * Converts a word into its plural form.
395  *
396  * @param string $word The singular word to pluralise.
397  *
398  * @return mixed An inflected string, or false if no rule could be applied.
399  *
400  * @since 12.1
401  */
402  public function toPlural($word)
403  {
404  // Try to get the cached plural form from the singular.
405  $cache = $this->_getCachedPlural($word);
406 
407  if ($cache !== false)
408  {
409  return $cache;
410  }
411 
412  // Check if the word is a known singular.
413  if ($this->_getCachedSingular($word))
414  {
415  return false;
416  }
417 
418  // Compute the inflection.
419  $inflected = $this->_matchRegexRule($word, 'plural');
420 
421  if ($inflected !== false)
422  {
423  $this->_setCache($word, $inflected);
424 
425  return $inflected;
426  }
427 
428  return false;
429  }
430 
431  /**
432  * Converts a word into its singular form.
433  *
434  * @param string $word The plural word to singularise.
435  *
436  * @return mixed An inflected string, or false if no rule could be applied.
437  *
438  * @since 12.1
439  */
440  public function toSingular($word)
441  {
442  // Try to get the cached singular form from the plural.
443  $cache = $this->_getCachedSingular($word);
444 
445  if ($cache !== false)
446  {
447  return $cache;
448  }
449 
450  // Check if the word is a known plural.
451  if ($this->_getCachedPlural($word))
452  {
453  return false;
454  }
455 
456  // Compute the inflection.
457  $inflected = $this->_matchRegexRule($word, 'singular');
458 
459  if ($inflected !== false)
460  {
461  $this->_setCache($inflected, $word);
462 
463  return $inflected;
464  }
465 
466  return false;
467  }
468 }