Adding extra charsets for ActionMailer unit tests, if you're looking to parse incomin...
[akelos.git] / lib / AkInflector.php
blob48b624d79f12a155b3efd67a24a274b5a3411c0c
1 <?php
2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4 // +----------------------------------------------------------------------+
5 // | Akelos Framework - http://www.akelos.org |
6 // +----------------------------------------------------------------------+
7 // | Copyright (c) 2002-2006, Akelos Media, S.L. & Bermi Ferrer Martinez |
8 // | Released under the GNU Lesser General Public License, see LICENSE.txt|
9 // +----------------------------------------------------------------------+
11 /**
12 * @package ActiveSupport
13 * @subpackage Inflector
14 * @author Bermi Ferrer <bermi a.t akelos c.om>
15 * @copyright Copyright (c) 2002-2006, Akelos Media, S.L. http://www.akelos.org
16 * @license GNU Lesser General Public License <http://www.gnu.org/copyleft/lesser.html>
19 /**
20 * AkInflector for pluralize and singularize English nouns.
22 * This AkInflector is a port of Ruby on Rails AkInflector.
24 * It can be really helpful for developers that want to
25 * create frameworks based on naming conventions rather than
26 * configurations.
28 * It was ported to PHP for the Akelos Framework, a
29 * multilingual Ruby on Rails like framework for PHP that will
30 * be launched soon.
32 * @author Bermi Ferrer Martinez <bermi a.t akelos c.om>
33 * @copyright Copyright (c) 2002-2006, Akelos Media, S.L. http://www.akelos.org
34 * @license GNU Lesser General Public License <http://www.gnu.org/copyleft/lesser.html>
36 class AkInflector
38 // ------ CLASS METHODS ------ //
40 // ---- Public methods ---- //
42 // {{{ pluralize()
44 /**
45 * Pluralizes English nouns.
47 * @access public
48 * @static
49 * @param string $word English noun to pluralize
50 * @return string Plural noun
52 function pluralize($word, $new_plural = null)
54 static $_cached;
55 if(isset($new_plural)){
56 $_cached[$word] = $new_plural;
57 return;
59 $_original_word = $word;
60 if(!isset($_cached[$_original_word])){
61 $plural = array(
62 '/(quiz)$/i' => '\1zes',
63 '/^(ox)$/i' => '\1en',
64 '/([m|l])ouse$/i' => '\1ice',
65 '/(matr|vert|ind)ix|ex$/i' => '\1ices',
66 '/(x|ch|ss|sh)$/i' => '\1es',
67 '/([^aeiouy]|qu)ies$/i' => '\1y',
68 '/([^aeiouy]|qu)y$/i' => '\1ies',
69 '/(hive)$/i' => '\1s',
70 '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
71 '/sis$/i' => 'ses',
72 '/([ti])um$/i' => '\1a',
73 '/(buffal|tomat)o$/i' => '\1oes',
74 '/(bu)s$/i' => '\1ses',
75 '/(alias|status)/i'=> '\1es',
76 '/(octop|vir)us$/i'=> '\1i',
77 '/(ax|test)is$/i'=> '\1es',
78 '/s$/i'=> 's',
79 '/$/'=> 's');
81 $uncountable = array('equipment', 'information', 'rice', 'money', 'species', 'series', 'fish', 'sheep');
83 $irregular = array(
84 'person' => 'people',
85 'man' => 'men',
86 'child' => 'children',
87 'sex' => 'sexes',
88 'move' => 'moves');
90 $lowercased_word = strtolower($word);
92 if (in_array($lowercased_word,$uncountable)){
93 return $word;
96 foreach ($irregular as $_plural=> $_singular){
97 if (preg_match('/('.$_plural.')$/i', $word, $arr)) {
98 $_cached[$_original_word] = preg_replace('/('.$_plural.')$/i', substr($arr[0],0,1).substr($_singular,1), $word);
99 return $_cached[$_original_word];
103 foreach ($plural as $rule => $replacement) {
104 if (preg_match($rule, $word)) {
105 $_cached[$_original_word] = preg_replace($rule, $replacement, $word);
106 return $_cached[$_original_word];
109 $_cached[$_original_word] = false;
110 return false;
112 return $_cached[$_original_word];
115 // }}}
116 // {{{ singularize()
119 * Singularizes English nouns.
121 * @access public
122 * @static
123 * @param string $word English noun to singularize
124 * @return string Singular noun.
126 function singularize($word, $new_singular = null)
128 static $_cached;
129 if(isset($new_singular)){
130 $_cached[$word] = $new_singular;
131 return;
133 $_original_word = $word;
134 if(!isset($_cached[$_original_word])){
135 $singular = array (
136 '/(quiz)zes$/i' => '\\1',
137 '/(matr)ices$/i' => '\\1ix',
138 '/(vert|ind)ices$/i' => '\\1ex',
139 '/^(ox)en/i' => '\\1',
140 '/(alias|status|wax)es$/i' => '\\1',
141 '/([octop|vir])i$/i' => '\\1us',
142 '/(cris|ax|test)es$/i' => '\\1is',
143 '/(shoe)s$/i' => '\\1',
144 '/(o)es$/i' => '\\1',
145 '/(bus)es$/i' => '\\1',
146 '/([m|l])ice$/i' => '\\1ouse',
147 '/(x|ch|ss|sh)es$/i' => '\\1',
148 '/(m)ovies$/i' => '\\1ovie',
149 '/(s)eries$/i' => '\\1eries',
150 '/([^aeiouy]|qu)ies$/i' => '\\1y',
151 '/([lr])ves$/i' => '\\1f',
152 '/(tive)s$/i' => '\\1',
153 '/(hive)s$/i' => '\\1',
154 '/([^f])ves$/i' => '\\1fe',
155 '/(^analy)ses$/i' => '\\1sis',
156 '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\\1\\2sis',
157 '/([ti])a$/i' => '\\1um',
158 '/(n)ews$/i' => '\\1ews',
159 '/s$/i' => '',
163 $uncountable = array('equipment', 'information', 'rice', 'money', 'species', 'series', 'fish', 'sheep','sms');
165 $irregular = array(
166 'person' => 'people',
167 'man' => 'men',
168 'child' => 'children',
169 'sex' => 'sexes',
170 'database' => 'databases',
171 'move' => 'moves');
173 $lowercased_word = strtolower($word);
175 if (in_array($lowercased_word,$uncountable)){
176 return $word;
179 foreach ($irregular as $_singular => $_plural){
180 if (preg_match('/('.$_plural.')$/i', $word, $arr)) {
181 $_cached[$_original_word] = preg_replace('/('.$_plural.')$/i', substr($arr[0],0,1).substr($_singular,1), $word);
182 return $_cached[$_original_word];
186 foreach ($singular as $rule => $replacement) {
187 if (preg_match($rule, $word)) {
188 $_cached[$_original_word] = preg_replace($rule, $replacement, $word);
189 return $_cached[$_original_word];
192 $_cached[$_original_word] = $word;
193 return $word;
195 return $_cached[$_original_word];
198 // }}}
199 // {{{ conditionalPlural()
202 * Get the plural form of a word if first parameter is greater than 1
204 * @param integer $numer_of_records
205 * @param string $word
206 * @return string Pluralized string when number of items is greater than 1
208 function conditionalPlural($numer_of_records, $word)
210 return $numer_of_records > 1 ? AkInflector::pluralize($word) : $word;
213 // }}}
214 // {{{ titleize()
217 * Converts an underscored or CamelCase word into a English
218 * sentence.
220 * The titleize function converts text like "WelcomePage",
221 * "welcome_page" or "welcome page" to this "Welcome
222 * Page".
223 * If second parameter is set to 'first' it will only
224 * capitalize the first character of the title.
226 * @access public
227 * @static
228 * @param string $word Word to format as tile
229 * @param string $uppercase If set to 'first' it will only uppercase the
230 * first character. Otherwise it will uppercase all
231 * the words in the title.
232 * @return string Text formatted as title
234 function titleize($word, $uppercase = '')
236 $uppercase = $uppercase == 'first' ? 'ucfirst' : 'ucwords';
237 return $uppercase(AkInflector::humanize(AkInflector::underscore($word)));
240 // }}}
241 // {{{ camelize()
244 * Returns given word as CamelCased
246 * Converts a word like "send_email" to "SendEmail". It
247 * will remove non alphanumeric character from the word, so
248 * "who's online" will be converted to "WhoSOnline"
250 * @access public
251 * @static
252 * @see variablize
253 * @param string $word Word to convert to camel case
254 * @return string UpperCamelCasedWord
256 function camelize($word)
258 static $_cached;
259 if(!isset($_cached[$word])){
260 if(preg_match_all('/\/(.?)/',$word,$got)){
261 foreach ($got[1] as $k=>$v){
262 $got[1][$k] = '::'.strtoupper($v);
264 $word = str_replace($got[0],$got[1],$word);
266 $_cached[$word] = str_replace(' ','',ucwords(preg_replace('/[^A-Z^a-z^0-9^:]+/',' ',$word)));
268 return $_cached[$word];
271 // }}}
272 // {{{ underscore()
275 * Converts a word "into_it_s_underscored_version"
277 * Convert any "CamelCased" or "ordinary Word" into an
278 * "underscored_word".
280 * This can be really useful for creating friendly URLs.
282 * @access public
283 * @static
284 * @param string $word Word to underscore
285 * @return string Underscored word
287 function underscore($word)
289 static $_cached;
290 if(!isset($_cached[$word])){
291 $_cached[$word] = strtolower(preg_replace(
292 array('/[^A-Z^a-z^0-9^\/]+/','/([a-z\d])([A-Z])/','/([A-Z]+)([A-Z][a-z])/'),
293 array('_','\1_\2','\1_\2'), $word));
295 return $_cached[$word];
299 // }}}
300 // {{{ humanize()
303 * Returns a human-readable string from $word
305 * Returns a human-readable string from $word, by replacing
306 * underscores with a space, and by upper-casing the initial
307 * character by default.
309 * If you need to uppercase all the words you just have to
310 * pass 'all' as a second parameter.
312 * @access public
313 * @static
314 * @param string $word String to "humanize"
315 * @param string $uppercase If set to 'all' it will uppercase all the words
316 * instead of just the first one.
317 * @return string Human-readable word
319 function humanize($word, $uppercase = '')
321 $uppercase = $uppercase == 'all' ? 'ucwords' : 'ucfirst';
322 return $uppercase(str_replace('_',' ',preg_replace('/_id$/', '',$word)));
325 // }}}
326 // {{{ variablize()
329 * Same as camelize but first char is lowercased
331 * Converts a word like "send_email" to "sendEmail". It
332 * will remove non alphanumeric character from the word, so
333 * "who's online" will be converted to "whoSOnline"
335 * @access public
336 * @static
337 * @see camelize
338 * @param string $word Word to lowerCamelCase
339 * @return string Returns a lowerCamelCasedWord
341 function variablize($word)
343 $word = AkInflector::camelize($word);
344 return strtolower($word[0]).substr($word,1);
347 // }}}
348 // {{{ tableize()
351 * Converts a class name to its table name according to rails
352 * naming conventions.
354 * Converts "Person" to "people"
356 * @access public
357 * @static
358 * @see classify
359 * @param string $class_name Class name for getting related table_name.
360 * @return string plural_table_name
362 function tableize($class_name)
364 return AkInflector::pluralize(AkInflector::underscore($class_name));
367 // }}}
368 // {{{ classify()
371 * Converts a table name to its class name according to Akelos
372 * naming conventions.
374 * Converts "people" to "Person"
376 * @access public
377 * @static
378 * @see tableize
379 * @param string $table_name Table name for getting related ClassName.
380 * @return string SingularClassName
382 function classify($table_name)
384 return AkInflector::camelize(AkInflector::singularize($table_name));
387 // }}}
388 // {{{ ordinalize()
391 * Converts number to its ordinal English form.
393 * This method converts 13 to 13th, 2 to 2nd ...
395 * @access public
396 * @static
397 * @param integer $number Number to get its ordinal value
398 * @return string Ordinal representation of given string.
400 function ordinalize($number)
402 if (in_array(($number % 100),range(11,13))){
403 return $number.'th';
404 }else{
405 switch (($number % 10)) {
406 case 1:
407 return $number.'st';
408 break;
409 case 2:
410 return $number.'nd';
411 break;
412 case 3:
413 return $number.'rd';
414 default:
415 return $number.'th';
416 break;
421 // }}}
424 * Removes the module name from a module/path, Module::name or Module_ControllerClassName.
426 * Example: AkInflector::demodulize('admin/dashboard_controller'); //=> dashboard_controller
427 * AkInflector::demodulize('Admin_DashboardController'); //=> DashboardController
428 * AkInflector::demodulize('Admin::Dashboard'); //=> Dashboard
430 function demodulize($module_name)
432 $module_name = str_replace('::', '/', $module_name);
433 return (strstr($module_name, '/') ? preg_replace('/^.*\//', '', $module_name) : (strstr($module_name, '_') ? substr($module_name, 1+strrpos($module_name,'_')) : $module_name));
437 * Transforms a string to its unaccented version.
438 * This might be useful for generating "friendly" URLs
440 function unaccent($text)
442 $map = array(
443 'À'=>'A', 'Á'=>'A', 'Â'=>'A', 'Ã'=>'A', 'Ä'=>'A', 'Å'=>'A', 'Æ'=>'A', 'Ç'=>'C',
444 'È'=>'E', 'É'=>'E', 'Ê'=>'E', 'Ë'=>'E', 'Ì'=>'I', 'Í'=>'I', 'Î'=>'I', 'Ï'=>'I',
445 'Ð'=>'D', 'Ñ'=>'N', 'Ò'=>'O', 'Ó'=>'O', 'Ô'=>'O', 'Õ'=>'O', 'Ö'=>'O', 'Ø'=>'O',
446 'Ù'=>'U', 'Ú'=>'U', 'Û'=>'U', 'Ü'=>'U', 'Ý'=>'Y', 'Þ'=>'T', 'ß'=>'s', 'à'=>'a',
447 'á'=>'a', 'â'=>'a', 'ã'=>'a', 'ä'=>'a', 'å'=>'a', 'æ'=>'a', 'ç'=>'c', 'è'=>'e',
448 'é'=>'e', 'ê'=>'e', 'ë'=>'e', 'ì'=>'i', 'í'=>'i', 'î'=>'i', 'ï'=>'i', 'ð'=>'e',
449 'ñ'=>'n', 'ò'=>'o', 'ó'=>'o', 'ô'=>'o', 'õ'=>'o', 'ö'=>'o', 'ø'=>'o', 'ù'=>'u',
450 'ú'=>'u', 'û'=>'u', 'ü'=>'u', 'ý'=>'y', 'þ'=>'t', 'ÿ'=>'y');
451 return str_replace(array_keys($map), array_values($map), $text);
455 function urlize($text)
457 return trim(AkInflector::underscore(AkInflector::unaccent($text)),'_');
461 * Returns $class_name in underscored form, with "_id" tacked on at the end.
462 * This is for use in dealing with the database.
464 * @param string $class_name
465 * @return string
467 function foreignKey($class_name, $separate_class_name_and_id_with_underscore = true)
469 return AkInflector::underscore(AkInflector::humanize(AkInflector::underscore($class_name))).($separate_class_name_and_id_with_underscore ? "_id" : "id");
472 function toControllerFilename($name)
474 $name = str_replace('::', '/', $name);
475 return AK_CONTROLLERS_DIR.DS.join(DS, array_map(array('AkInflector','underscore'), strstr($name, '/') ? explode('/', $name) : array($name))).'_controller.php';
478 function toModelFilename($name)
480 return AK_MODELS_DIR.DS.AkInflector::underscore($name).'.php';
483 function toHelperFilename($name)
485 return AK_APP_DIR.DS.'helpers'.DS.AkInflector::underscore($name).'_helper.php';
488 function toFullName($name, $correct)
490 if (strstr($name, '_') && (strtolower($name) == $name)){
491 return AkInflector::camelize($name);
494 if (preg_match("/^(. * )({$correct})$/i", $name, $reg)){
495 if ($reg[2] == $correct){
496 return $name;
497 }else{
498 return ucfirst($reg[1].$correct);
500 }else{
501 return ucfirst($name.$correct);
505 function is_singular($singular)
507 return AkInflector::singularize(AkInflector::pluralize($singular)) == $singular;
510 function is_plural($plural)
512 return AkInflector::pluralize(AkInflector::singularize($plural)) == $plural;