3a951af8d4a43d88f1b8aeed56c8221ef4ebe7fa
[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|gulf)$/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',
71             '.*deer',
72             '.*fish',
73             '.*measles',
74             '.*ois',
75             '.*pox',
76             '.*sheep',
77             'people',
78             'cookie',
79             'police',
80         ),
81         'irregular' => array(
82             'atlas' => 'atlases',
83             'axe' => 'axes',
84             'beef' => 'beefs',
85             'brother' => 'brothers',
86             'cafe' => 'cafes',
87             'chateau' => 'chateaux',
88             'niveau' => 'niveaux',
89             'child' => 'children',
90             'cookie' => 'cookies',
91             'corpus' => 'corpuses',
92             'cow' => 'cows',
93             'criterion' => 'criteria',
94             'curriculum' => 'curricula',
95             'demo' => 'demos',
96             'domino' => 'dominoes',
97             'echo' => 'echoes',
98             'foot' => 'feet',
99             'fungus' => 'fungi',
100             'ganglion' => 'ganglions',
101             'genie' => 'genies',
102             'genus' => 'genera',
103             'graffito' => 'graffiti',
104             'hippopotamus' => 'hippopotami',
105             'hoof' => 'hoofs',
106             'human' => 'humans',
107             'iris' => 'irises',
108             'larva' => 'larvae',
109             'leaf' => 'leaves',
110             'loaf' => 'loaves',
111             'man' => 'men',
112             'medium' => 'media',
113             'memorandum' => 'memoranda',
114             'money' => 'monies',
115             'mongoose' => 'mongooses',
116             'motto' => 'mottoes',
117             'move' => 'moves',
118             'mythos' => 'mythoi',
119             'niche' => 'niches',
120             'nucleus' => 'nuclei',
121             'numen' => 'numina',
122             'occiput' => 'occiputs',
123             'octopus' => 'octopuses',
124             'opus' => 'opuses',
125             'ox' => 'oxen',
126             'passerby' => 'passersby',
127             'penis' => 'penises',
128             'person' => 'people',
129             'plateau' => 'plateaux',
130             'runner-up' => 'runners-up',
131             'sex' => 'sexes',
132             'soliloquy' => 'soliloquies',
133             'son-in-law' => 'sons-in-law',
134             'syllabus' => 'syllabi',
135             'testis' => 'testes',
136             'thief' => 'thieves',
137             'tooth' => 'teeth',
138             'tornado' => 'tornadoes',
139             'trilby' => 'trilbys',
140             'turf' => 'turfs',
141             'volcano' => 'volcanoes',
142         )
143     );
144
145     /**
146      * Singular inflector rules.
147      *
148      * @var array
149      */
150     private static $singular = array(
151         'rules' => array(
152             '/(s)tatuses$/i' => '\1\2tatus',
153             '/^(.*)(menu)s$/i' => '\1\2',
154             '/(quiz)zes$/i' => '\\1',
155             '/(matr)ices$/i' => '\1ix',
156             '/(vert|ind)ices$/i' => '\1ex',
157             '/^(ox)en/i' => '\1',
158             '/(alias)(es)*$/i' => '\1',
159             '/(buffal|her|potat|tomat|volcan)oes$/i' => '\1o',
160             '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
161             '/([ftw]ax)es/i' => '\1',
162             '/(analys|ax|cris|test|thes)es$/i' => '\1is',
163             '/(shoe|slave)s$/i' => '\1',
164             '/(o)es$/i' => '\1',
165             '/ouses$/' => 'ouse',
166             '/([^a])uses$/' => '\1us',
167             '/([m|l])ice$/i' => '\1ouse',
168             '/(x|ch|ss|sh)es$/i' => '\1',
169             '/(m)ovies$/i' => '\1\2ovie',
170             '/(s)eries$/i' => '\1\2eries',
171             '/([^aeiouy]|qu)ies$/i' => '\1y',
172             '/([lr])ves$/i' => '\1f',
173             '/(tive)s$/i' => '\1',
174             '/(hive)s$/i' => '\1',
175             '/(drive)s$/i' => '\1',
176             '/(dive)s$/i' => '\1',
177             '/([^fo])ves$/i' => '\1fe',
178             '/(^analy)ses$/i' => '\1sis',
179             '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
180             '/([ti])a$/i' => '\1um',
181             '/(p)eople$/i' => '\1\2erson',
182             '/(m)en$/i' => '\1an',
183             '/(c)hildren$/i' => '\1\2hild',
184             '/(f)eet$/i' => '\1oot',
185             '/(n)ews$/i' => '\1\2ews',
186             '/eaus$/' => 'eau',
187             '/^(.*us)$/' => '\\1',
188             '/s$/i' => '',
189         ),
190         'uninflected' => array(
191             '.*[nrlm]ese',
192             '.*deer',
193             '.*fish',
194             '.*measles',
195             '.*ois',
196             '.*pox',
197             '.*sheep',
198             '.*ss',
199             'police',
200             'pants',
201             'clothes',
202         ),
203         'irregular' => array(
204             'caches'    => 'cache',
205             'criteria'  => 'criterion',
206             'curves'    => 'curve',
207             'emphases'  => 'emphasis',
208             'foes'      => 'foe',
209             'hoaxes'    => 'hoax',
210             'media'     => 'medium',
211             'neuroses'  => 'neurosis',
212             'waves'     => 'wave',
213             'oases'     => 'oasis',
214         )
215     );
216
217     /**
218      * Words that should not be inflected.
219      *
220      * @var array
221      */
222     private static $uninflected = array(
223         'Amoyese', 'bison', 'Borghese', 'bream', 'breeches', 'britches', 'buffalo', 'cantus',
224         'carp', 'chassis', 'clippers', 'cod', 'coitus', 'Congoese', 'contretemps', 'corps',
225         'debris', 'diabetes', 'djinn', 'eland', 'elk', 'equipment', 'Faroese', 'flounder',
226         'Foochowese', 'Furniture', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'graffiti',
227         'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings',
228         'jackanapes', 'Kiplingese', 'Kongoese', 'Lucchese', 'Luggage', 'mackerel', 'Maltese', '.*?media',
229         'mews', 'moose', 'mumps', 'Nankingese', 'news', 'nexus', 'Niasese',
230         'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'pliers', 'Portuguese',
231         'proceedings', 'rabies', 'rice', 'rhinoceros', 'salmon', 'Sarawakese', 'scissors',
232         'sea[- ]bass', 'series', 'Shavese', 'shears', 'siemens', 'species', 'staff', 'swine',
233         'testes', 'trousers', 'trout', 'tuna', 'Vermontese', 'Wenchowese', 'whiting',
234         'wildebeest', 'Yengeese'
235     );
236
237     /**
238      * Method cache array.
239      *
240      * @var array
241      */
242     private static $cache = array();
243
244     /**
245      * The initial state of Inflector so reset() works.
246      *
247      * @var array
248      */
249     private static $initialState = array();
250
251     /**
252      * Converts a word into the format for a Doctrine table name. Converts 'ModelName' to 'model_name'.
253      *
254      * @param string $word The word to tableize.
255      *
256      * @return string The tableized word.
257      */
258     public static function tableize($word)
259     {
260         return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $word));
261     }
262
263     /**
264      * Converts a word into the format for a Doctrine class name. Converts 'table_name' to 'TableName'.
265      *
266      * @param string $word The word to classify.
267      *
268      * @return string The classified word.
269      */
270     public static function classify($word)
271     {
272         return str_replace(' ', '', ucwords(strtr($word, '_-', '  ')));
273     }
274
275     /**
276      * Camelizes a word. This uses the classify() method and turns the first character to lowercase.
277      *
278      * @param string $word The word to camelize.
279      *
280      * @return string The camelized word.
281      */
282     public static function camelize($word)
283     {
284         return lcfirst(self::classify($word));
285     }
286
287     /**
288      * Uppercases words with configurable delimeters between words.
289      *
290      * Takes a string and capitalizes all of the words, like PHP's built-in
291      * ucwords function.  This extends that behavior, however, by allowing the
292      * word delimeters to be configured, rather than only separating on
293      * whitespace.
294      *
295      * Here is an example:
296      * <code>
297      * <?php
298      * $string = 'top-o-the-morning to all_of_you!';
299      * echo \Doctrine\Common\Inflector\Inflector::ucwords($string);
300      * // Top-O-The-Morning To All_of_you!
301      *
302      * echo \Doctrine\Common\Inflector\Inflector::ucwords($string, '-_ ');
303      * // Top-O-The-Morning To All_Of_You!
304      * ?>
305      * </code>
306      *
307      * @param string $string The string to operate on.
308      * @param string $delimiters A list of word separators.
309      *
310      * @return string The string with all delimeter-separated words capitalized.
311      */
312     public static function ucwords($string, $delimiters = " \n\t\r\0\x0B-")
313     {
314         return preg_replace_callback(
315             '/[^' . preg_quote($delimiters, '/') . ']+/',
316             function($matches) {
317                 return ucfirst($matches[0]);
318             },
319             $string
320         );
321     }
322
323     /**
324      * Clears Inflectors inflected value caches, and resets the inflection
325      * rules to the initial values.
326      *
327      * @return void
328      */
329     public static function reset()
330     {
331         if (empty(self::$initialState)) {
332             self::$initialState = get_class_vars('Inflector');
333
334             return;
335         }
336
337         foreach (self::$initialState as $key => $val) {
338             if ($key != 'initialState') {
339                 self::${$key} = $val;
340             }
341         }
342     }
343
344     /**
345      * Adds custom inflection $rules, of either 'plural' or 'singular' $type.
346      *
347      * ### Usage:
348      *
349      * {{{
350      * Inflector::rules('plural', array('/^(inflect)or$/i' => '\1ables'));
351      * Inflector::rules('plural', array(
352      *     'rules' => array('/^(inflect)ors$/i' => '\1ables'),
353      *     'uninflected' => array('dontinflectme'),
354      *     'irregular' => array('red' => 'redlings')
355      * ));
356      * }}}
357      *
358      * @param string  $type  The type of inflection, either 'plural' or 'singular'
359      * @param array   $rules An array of rules to be added.
360      * @param boolean $reset If true, will unset default inflections for all
361      *                       new rules that are being defined in $rules.
362      *
363      * @return void
364      */
365     public static function rules($type, $rules, $reset = false)
366     {
367         foreach ($rules as $rule => $pattern) {
368             if ( ! is_array($pattern)) {
369                 continue;
370             }
371
372             if ($reset) {
373                 self::${$type}[$rule] = $pattern;
374             } else {
375                 self::${$type}[$rule] = ($rule === 'uninflected')
376                     ? array_merge($pattern, self::${$type}[$rule])
377                     : $pattern + self::${$type}[$rule];
378             }
379
380             unset($rules[$rule], self::${$type}['cache' . ucfirst($rule)]);
381
382             if (isset(self::${$type}['merged'][$rule])) {
383                 unset(self::${$type}['merged'][$rule]);
384             }
385
386             if ($type === 'plural') {
387                 self::$cache['pluralize'] = self::$cache['tableize'] = array();
388             } elseif ($type === 'singular') {
389                 self::$cache['singularize'] = array();
390             }
391         }
392
393         self::${$type}['rules'] = $rules + self::${$type}['rules'];
394     }
395
396     /**
397      * Returns a word in plural form.
398      *
399      * @param string $word The word in singular form.
400      *
401      * @return string The word in plural form.
402      */
403     public static function pluralize($word)
404     {
405         if (isset(self::$cache['pluralize'][$word])) {
406             return self::$cache['pluralize'][$word];
407         }
408
409         if (!isset(self::$plural['merged']['irregular'])) {
410             self::$plural['merged']['irregular'] = self::$plural['irregular'];
411         }
412
413         if (!isset(self::$plural['merged']['uninflected'])) {
414             self::$plural['merged']['uninflected'] = array_merge(self::$plural['uninflected'], self::$uninflected);
415         }
416
417         if (!isset(self::$plural['cacheUninflected']) || !isset(self::$plural['cacheIrregular'])) {
418             self::$plural['cacheUninflected'] = '(?:' . implode('|', self::$plural['merged']['uninflected']) . ')';
419             self::$plural['cacheIrregular']   = '(?:' . implode('|', array_keys(self::$plural['merged']['irregular'])) . ')';
420         }
421
422         if (preg_match('/(.*)\\b(' . self::$plural['cacheIrregular'] . ')$/i', $word, $regs)) {
423             self::$cache['pluralize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$plural['merged']['irregular'][strtolower($regs[2])], 1);
424
425             return self::$cache['pluralize'][$word];
426         }
427
428         if (preg_match('/^(' . self::$plural['cacheUninflected'] . ')$/i', $word, $regs)) {
429             self::$cache['pluralize'][$word] = $word;
430
431             return $word;
432         }
433
434         foreach (self::$plural['rules'] as $rule => $replacement) {
435             if (preg_match($rule, $word)) {
436                 self::$cache['pluralize'][$word] = preg_replace($rule, $replacement, $word);
437
438                 return self::$cache['pluralize'][$word];
439             }
440         }
441     }
442
443     /**
444      * Returns a word in singular form.
445      *
446      * @param string $word The word in plural form.
447      *
448      * @return string The word in singular form.
449      */
450     public static function singularize($word)
451     {
452         if (isset(self::$cache['singularize'][$word])) {
453             return self::$cache['singularize'][$word];
454         }
455
456         if (!isset(self::$singular['merged']['uninflected'])) {
457             self::$singular['merged']['uninflected'] = array_merge(
458                 self::$singular['uninflected'],
459                 self::$uninflected
460             );
461         }
462
463         if (!isset(self::$singular['merged']['irregular'])) {
464             self::$singular['merged']['irregular'] = array_merge(
465                 self::$singular['irregular'],
466                 array_flip(self::$plural['irregular'])
467             );
468         }
469
470         if (!isset(self::$singular['cacheUninflected']) || !isset(self::$singular['cacheIrregular'])) {
471             self::$singular['cacheUninflected'] = '(?:' . join('|', self::$singular['merged']['uninflected']) . ')';
472             self::$singular['cacheIrregular'] = '(?:' . join('|', array_keys(self::$singular['merged']['irregular'])) . ')';
473         }
474
475         if (preg_match('/(.*)\\b(' . self::$singular['cacheIrregular'] . ')$/i', $word, $regs)) {
476             self::$cache['singularize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$singular['merged']['irregular'][strtolower($regs[2])], 1);
477
478             return self::$cache['singularize'][$word];
479         }
480
481         if (preg_match('/^(' . self::$singular['cacheUninflected'] . ')$/i', $word, $regs)) {
482             self::$cache['singularize'][$word] = $word;
483
484             return $word;
485         }
486
487         foreach (self::$singular['rules'] as $rule => $replacement) {
488             if (preg_match($rule, $word)) {
489                 self::$cache['singularize'][$word] = preg_replace($rule, $replacement, $word);
490
491                 return self::$cache['singularize'][$word];
492             }
493         }
494
495         self::$cache['singularize'][$word] = $word;
496
497         return $word;
498     }
499 }