Version 1
[yaffs-website] / vendor / doctrine / inflector / lib / Doctrine / Common / Inflector / Inflector.php
1 <?php
2 /*
3  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14  *
15  * This software consists of voluntary contributions made by many individuals
16  * and is licensed under the MIT license. For more information, see
17  * <http://www.doctrine-project.org>.
18  */
19
20 namespace Doctrine\Common\Inflector;
21
22 /**
23  * Doctrine inflector has static methods for inflecting text.
24  *
25  * The methods in these classes are from several different sources collected
26  * across several different php projects and several different authors. The
27  * original author names and emails are not known.
28  *
29  * Pluralize & Singularize implementation are borrowed from CakePHP with some modifications.
30  *
31  * @link   www.doctrine-project.org
32  * @since  1.0
33  * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
34  * @author Jonathan H. Wage <jonwage@gmail.com>
35  */
36 class Inflector
37 {
38     /**
39      * Plural inflector rules.
40      *
41      * @var array
42      */
43     private static $plural = array(
44         'rules' => array(
45             '/(s)tatus$/i' => '\1\2tatuses',
46             '/(quiz)$/i' => '\1zes',
47             '/^(ox)$/i' => '\1\2en',
48             '/([m|l])ouse$/i' => '\1ice',
49             '/(matr|vert|ind)(ix|ex)$/i' => '\1ices',
50             '/(x|ch|ss|sh)$/i' => '\1es',
51             '/([^aeiouy]|qu)y$/i' => '\1ies',
52             '/(hive)$/i' => '\1s',
53             '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
54             '/sis$/i' => 'ses',
55             '/([ti])um$/i' => '\1a',
56             '/(p)erson$/i' => '\1eople',
57             '/(m)an$/i' => '\1en',
58             '/(c)hild$/i' => '\1hildren',
59             '/(f)oot$/i' => '\1eet',
60             '/(buffal|her|potat|tomat|volcan)o$/i' => '\1\2oes',
61             '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i',
62             '/us$/i' => 'uses',
63             '/(alias)$/i' => '\1es',
64             '/(analys|ax|cris|test|thes)is$/i' => '\1es',
65             '/s$/' => 's',
66             '/^$/' => '',
67             '/$/' => 's',
68         ),
69         'uninflected' => array(
70             '.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', 'people', 'cookie'
71         ),
72         'irregular' => array(
73             'atlas' => 'atlases',
74             'axe' => 'axes',
75             'beef' => 'beefs',
76             'brother' => 'brothers',
77             'cafe' => 'cafes',
78             'chateau' => 'chateaux',
79             'child' => 'children',
80             'cookie' => 'cookies',
81             'corpus' => 'corpuses',
82             'cow' => 'cows',
83             'criterion' => 'criteria',
84             'curriculum' => 'curricula',
85             'demo' => 'demos',
86             'domino' => 'dominoes',
87             'echo' => 'echoes',
88             'foot' => 'feet',
89             'fungus' => 'fungi',
90             'ganglion' => 'ganglions',
91             'genie' => 'genies',
92             'genus' => 'genera',
93             'graffito' => 'graffiti',
94             'hippopotamus' => 'hippopotami',
95             'hoof' => 'hoofs',
96             'human' => 'humans',
97             'iris' => 'irises',
98             'leaf' => 'leaves',
99             'loaf' => 'loaves',
100             'man' => 'men',
101             'medium' => 'media',
102             'memorandum' => 'memoranda',
103             'money' => 'monies',
104             'mongoose' => 'mongooses',
105             'motto' => 'mottoes',
106             'move' => 'moves',
107             'mythos' => 'mythoi',
108             'niche' => 'niches',
109             'nucleus' => 'nuclei',
110             'numen' => 'numina',
111             'occiput' => 'occiputs',
112             'octopus' => 'octopuses',
113             'opus' => 'opuses',
114             'ox' => 'oxen',
115             'penis' => 'penises',
116             'person' => 'people',
117             'plateau' => 'plateaux',
118             'runner-up' => 'runners-up',
119             'sex' => 'sexes',
120             'soliloquy' => 'soliloquies',
121             'son-in-law' => 'sons-in-law',
122             'syllabus' => 'syllabi',
123             'testis' => 'testes',
124             'thief' => 'thieves',
125             'tooth' => 'teeth',
126             'tornado' => 'tornadoes',
127             'trilby' => 'trilbys',
128             'turf' => 'turfs',
129             'volcano' => 'volcanoes',
130         )
131     );
132
133     /**
134      * Singular inflector rules.
135      *
136      * @var array
137      */
138     private static $singular = array(
139         'rules' => array(
140             '/(s)tatuses$/i' => '\1\2tatus',
141             '/^(.*)(menu)s$/i' => '\1\2',
142             '/(quiz)zes$/i' => '\\1',
143             '/(matr)ices$/i' => '\1ix',
144             '/(vert|ind)ices$/i' => '\1ex',
145             '/^(ox)en/i' => '\1',
146             '/(alias)(es)*$/i' => '\1',
147             '/(buffal|her|potat|tomat|volcan)oes$/i' => '\1o',
148             '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
149             '/([ftw]ax)es/i' => '\1',
150             '/(analys|ax|cris|test|thes)es$/i' => '\1is',
151             '/(shoe|slave)s$/i' => '\1',
152             '/(o)es$/i' => '\1',
153             '/ouses$/' => 'ouse',
154             '/([^a])uses$/' => '\1us',
155             '/([m|l])ice$/i' => '\1ouse',
156             '/(x|ch|ss|sh)es$/i' => '\1',
157             '/(m)ovies$/i' => '\1\2ovie',
158             '/(s)eries$/i' => '\1\2eries',
159             '/([^aeiouy]|qu)ies$/i' => '\1y',
160             '/([lr])ves$/i' => '\1f',
161             '/(tive)s$/i' => '\1',
162             '/(hive)s$/i' => '\1',
163             '/(drive)s$/i' => '\1',
164             '/([^fo])ves$/i' => '\1fe',
165             '/(^analy)ses$/i' => '\1sis',
166             '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
167             '/([ti])a$/i' => '\1um',
168             '/(p)eople$/i' => '\1\2erson',
169             '/(m)en$/i' => '\1an',
170             '/(c)hildren$/i' => '\1\2hild',
171             '/(f)eet$/i' => '\1oot',
172             '/(n)ews$/i' => '\1\2ews',
173             '/eaus$/' => 'eau',
174             '/^(.*us)$/' => '\\1',
175             '/s$/i' => '',
176         ),
177         'uninflected' => array(
178             '.*[nrlm]ese',
179             '.*deer',
180             '.*fish',
181             '.*measles',
182             '.*ois',
183             '.*pox',
184             '.*sheep',
185             '.*ss',
186         ),
187         'irregular' => array(
188             'criteria'  => 'criterion',
189             'curves'    => 'curve',
190             'emphases'  => 'emphasis',
191             'foes'      => 'foe',
192             'hoaxes'    => 'hoax',
193             'media'     => 'medium',
194             'neuroses'  => 'neurosis',
195             'waves'     => 'wave',
196             'oases'     => 'oasis',
197         )
198     );
199
200     /**
201      * Words that should not be inflected.
202      *
203      * @var array
204      */
205     private static $uninflected = array(
206         'Amoyese', 'bison', 'Borghese', 'bream', 'breeches', 'britches', 'buffalo', 'cantus',
207         'carp', 'chassis', 'clippers', 'cod', 'coitus', 'Congoese', 'contretemps', 'corps',
208         'debris', 'diabetes', 'djinn', 'eland', 'elk', 'equipment', 'Faroese', 'flounder',
209         'Foochowese', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'graffiti',
210         'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings',
211         'jackanapes', 'Kiplingese', 'Kongoese', 'Lucchese', 'mackerel', 'Maltese', '.*?media',
212         'mews', 'moose', 'mumps', 'Nankingese', 'news', 'nexus', 'Niasese',
213         'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'pliers', 'Portuguese',
214         'proceedings', 'rabies', 'rice', 'rhinoceros', 'salmon', 'Sarawakese', 'scissors',
215         'sea[- ]bass', 'series', 'Shavese', 'shears', 'siemens', 'species', 'staff', 'swine',
216         'testes', 'trousers', 'trout', 'tuna', 'Vermontese', 'Wenchowese', 'whiting',
217         'wildebeest', 'Yengeese'
218     );
219
220     /**
221      * Method cache array.
222      *
223      * @var array
224      */
225     private static $cache = array();
226
227     /**
228      * The initial state of Inflector so reset() works.
229      *
230      * @var array
231      */
232     private static $initialState = array();
233
234     /**
235      * Converts a word into the format for a Doctrine table name. Converts 'ModelName' to 'model_name'.
236      *
237      * @param string $word The word to tableize.
238      *
239      * @return string The tableized word.
240      */
241     public static function tableize($word)
242     {
243         return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $word));
244     }
245
246     /**
247      * Converts a word into the format for a Doctrine class name. Converts 'table_name' to 'TableName'.
248      *
249      * @param string $word The word to classify.
250      *
251      * @return string The classified word.
252      */
253     public static function classify($word)
254     {
255         return str_replace(" ", "", ucwords(strtr($word, "_-", "  ")));
256     }
257
258     /**
259      * Camelizes a word. This uses the classify() method and turns the first character to lowercase.
260      *
261      * @param string $word The word to camelize.
262      *
263      * @return string The camelized word.
264      */
265     public static function camelize($word)
266     {
267         return lcfirst(self::classify($word));
268     }
269
270     /**
271      * Uppercases words with configurable delimeters between words.
272      *
273      * Takes a string and capitalizes all of the words, like PHP's built-in
274      * ucwords function.  This extends that behavior, however, by allowing the
275      * word delimeters to be configured, rather than only separating on
276      * whitespace.
277      *
278      * Here is an example:
279      * <code>
280      * <?php
281      * $string = 'top-o-the-morning to all_of_you!';
282      * echo \Doctrine\Common\Inflector\Inflector::ucwords($string);
283      * // Top-O-The-Morning To All_of_you!
284      *
285      * echo \Doctrine\Common\Inflector\Inflector::ucwords($string, '-_ ');
286      * // Top-O-The-Morning To All_Of_You!
287      * ?>
288      * </code>
289      *
290      * @param string $string The string to operate on.
291      * @param string $delimiters A list of word separators.
292      *
293      * @return string The string with all delimeter-separated words capitalized.
294      */
295     public static function ucwords($string, $delimiters = " \n\t\r\0\x0B-")
296     {
297         return preg_replace_callback(
298             '/[^' . preg_quote($delimiters, '/') . ']+/',
299             function($matches) {
300                 return ucfirst($matches[0]);
301             },
302             $string
303         );
304     }
305
306     /**
307      * Clears Inflectors inflected value caches, and resets the inflection
308      * rules to the initial values.
309      *
310      * @return void
311      */
312     public static function reset()
313     {
314         if (empty(self::$initialState)) {
315             self::$initialState = get_class_vars('Inflector');
316
317             return;
318         }
319
320         foreach (self::$initialState as $key => $val) {
321             if ($key != 'initialState') {
322                 self::${$key} = $val;
323             }
324         }
325     }
326
327     /**
328      * Adds custom inflection $rules, of either 'plural' or 'singular' $type.
329      *
330      * ### Usage:
331      *
332      * {{{
333      * Inflector::rules('plural', array('/^(inflect)or$/i' => '\1ables'));
334      * Inflector::rules('plural', array(
335      *     'rules' => array('/^(inflect)ors$/i' => '\1ables'),
336      *     'uninflected' => array('dontinflectme'),
337      *     'irregular' => array('red' => 'redlings')
338      * ));
339      * }}}
340      *
341      * @param string  $type  The type of inflection, either 'plural' or 'singular'
342      * @param array   $rules An array of rules to be added.
343      * @param boolean $reset If true, will unset default inflections for all
344      *                       new rules that are being defined in $rules.
345      *
346      * @return void
347      */
348     public static function rules($type, $rules, $reset = false)
349     {
350         foreach ($rules as $rule => $pattern) {
351             if ( ! is_array($pattern)) {
352                 continue;
353             }
354
355             if ($reset) {
356                 self::${$type}[$rule] = $pattern;
357             } else {
358                 self::${$type}[$rule] = ($rule === 'uninflected')
359                     ? array_merge($pattern, self::${$type}[$rule])
360                     : $pattern + self::${$type}[$rule];
361             }
362
363             unset($rules[$rule], self::${$type}['cache' . ucfirst($rule)]);
364
365             if (isset(self::${$type}['merged'][$rule])) {
366                 unset(self::${$type}['merged'][$rule]);
367             }
368
369             if ($type === 'plural') {
370                 self::$cache['pluralize'] = self::$cache['tableize'] = array();
371             } elseif ($type === 'singular') {
372                 self::$cache['singularize'] = array();
373             }
374         }
375
376         self::${$type}['rules'] = $rules + self::${$type}['rules'];
377     }
378
379     /**
380      * Returns a word in plural form.
381      *
382      * @param string $word The word in singular form.
383      *
384      * @return string The word in plural form.
385      */
386     public static function pluralize($word)
387     {
388         if (isset(self::$cache['pluralize'][$word])) {
389             return self::$cache['pluralize'][$word];
390         }
391
392         if (!isset(self::$plural['merged']['irregular'])) {
393             self::$plural['merged']['irregular'] = self::$plural['irregular'];
394         }
395
396         if (!isset(self::$plural['merged']['uninflected'])) {
397             self::$plural['merged']['uninflected'] = array_merge(self::$plural['uninflected'], self::$uninflected);
398         }
399
400         if (!isset(self::$plural['cacheUninflected']) || !isset(self::$plural['cacheIrregular'])) {
401             self::$plural['cacheUninflected'] = '(?:' . implode('|', self::$plural['merged']['uninflected']) . ')';
402             self::$plural['cacheIrregular']   = '(?:' . implode('|', array_keys(self::$plural['merged']['irregular'])) . ')';
403         }
404
405         if (preg_match('/(.*)\\b(' . self::$plural['cacheIrregular'] . ')$/i', $word, $regs)) {
406             self::$cache['pluralize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$plural['merged']['irregular'][strtolower($regs[2])], 1);
407             
408             return self::$cache['pluralize'][$word];
409         }
410
411         if (preg_match('/^(' . self::$plural['cacheUninflected'] . ')$/i', $word, $regs)) {
412             self::$cache['pluralize'][$word] = $word;
413
414             return $word;
415         }
416
417         foreach (self::$plural['rules'] as $rule => $replacement) {
418             if (preg_match($rule, $word)) {
419                 self::$cache['pluralize'][$word] = preg_replace($rule, $replacement, $word);
420
421                 return self::$cache['pluralize'][$word];
422             }
423         }
424     }
425
426     /**
427      * Returns a word in singular form.
428      *
429      * @param string $word The word in plural form.
430      *
431      * @return string The word in singular form.
432      */
433     public static function singularize($word)
434     {
435         if (isset(self::$cache['singularize'][$word])) {
436             return self::$cache['singularize'][$word];
437         }
438
439         if (!isset(self::$singular['merged']['uninflected'])) {
440             self::$singular['merged']['uninflected'] = array_merge(
441                 self::$singular['uninflected'],
442                 self::$uninflected
443             );
444         }
445
446         if (!isset(self::$singular['merged']['irregular'])) {
447             self::$singular['merged']['irregular'] = array_merge(
448                 self::$singular['irregular'],
449                 array_flip(self::$plural['irregular'])
450             );
451         }
452
453         if (!isset(self::$singular['cacheUninflected']) || !isset(self::$singular['cacheIrregular'])) {
454             self::$singular['cacheUninflected'] = '(?:' . join('|', self::$singular['merged']['uninflected']) . ')';
455             self::$singular['cacheIrregular'] = '(?:' . join('|', array_keys(self::$singular['merged']['irregular'])) . ')';
456         }
457
458         if (preg_match('/(.*)\\b(' . self::$singular['cacheIrregular'] . ')$/i', $word, $regs)) {
459             self::$cache['singularize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$singular['merged']['irregular'][strtolower($regs[2])], 1);
460             
461             return self::$cache['singularize'][$word];
462         }
463
464         if (preg_match('/^(' . self::$singular['cacheUninflected'] . ')$/i', $word, $regs)) {
465             self::$cache['singularize'][$word] = $word;
466
467             return $word;
468         }
469
470         foreach (self::$singular['rules'] as $rule => $replacement) {
471             if (preg_match($rule, $word)) {
472                 self::$cache['singularize'][$word] = preg_replace($rule, $replacement, $word);
473
474                 return self::$cache['singularize'][$word];
475             }
476         }
477
478         self::$cache['singularize'][$word] = $word;
479
480         return $word;
481     }
482 }