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