Fixes #149
[akelos.git] / lib / AkInflector.php
blob54f8cdf6ae42590007910d4b7f0afc90c980a10f
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 foreach ($uncountable as $_uncountable){
93 if(substr($lowercased_word,(-1*strlen($_uncountable))) == $_uncountable){
94 $_cached[$_original_word] = $word;
95 return $word;
99 foreach ($irregular as $_plural=> $_singular){
100 if (preg_match('/('.$_plural.')$/i', $word, $arr)) {
101 $_cached[$_original_word] = preg_replace('/('.$_plural.')$/i', substr($arr[0],0,1).substr($_singular,1), $word);
102 return $_cached[$_original_word];
106 foreach ($plural as $rule => $replacement) {
107 if (preg_match($rule, $word)) {
108 $_cached[$_original_word] = preg_replace($rule, $replacement, $word);
109 return $_cached[$_original_word];
112 $_cached[$_original_word] = false;
113 return false;
115 return $_cached[$_original_word];
118 // }}}
119 // {{{ singularize()
122 * Singularizes English nouns.
124 * @access public
125 * @static
126 * @param string $word English noun to singularize
127 * @return string Singular noun.
129 function singularize($word, $new_singular = null)
131 static $_cached;
132 if(isset($new_singular)){
133 $_cached[$word] = $new_singular;
134 return;
136 $_original_word = $word;
137 if(!isset($_cached[$_original_word])){
138 $singular = array (
139 '/(quiz)zes$/i' => '\\1',
140 '/(matr)ices$/i' => '\\1ix',
141 '/(vert|ind)ices$/i' => '\\1ex',
142 '/^(ox)en/i' => '\\1',
143 '/(alias|status|wax)es$/i' => '\\1',
144 '/([octop|vir])i$/i' => '\\1us',
145 '/(cris|ax|test)es$/i' => '\\1is',
146 '/(shoe)s$/i' => '\\1',
147 '/(o)es$/i' => '\\1',
148 '/(bus)es$/i' => '\\1',
149 '/([m|l])ice$/i' => '\\1ouse',
150 '/(x|ch|ss|sh)es$/i' => '\\1',
151 '/(m)ovies$/i' => '\\1ovie',
152 '/(s)eries$/i' => '\\1eries',
153 '/([^aeiouy]|qu)ies$/i' => '\\1y',
154 '/([lr])ves$/i' => '\\1f',
155 '/(tive)s$/i' => '\\1',
156 '/(hive)s$/i' => '\\1',
157 '/([^f])ves$/i' => '\\1fe',
158 '/(^analy)ses$/i' => '\\1sis',
159 '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\\1\\2sis',
160 '/([ti])a$/i' => '\\1um',
161 '/(n)ews$/i' => '\\1ews',
162 '/s$/i' => '',
166 $uncountable = array('equipment', 'information', 'rice', 'money', 'species', 'series', 'fish', 'sheep','sms');
168 $irregular = array(
169 'person' => 'people',
170 'man' => 'men',
171 'child' => 'children',
172 'sex' => 'sexes',
173 'database' => 'databases',
174 'move' => 'moves');
176 $lowercased_word = strtolower($word);
177 foreach ($uncountable as $_uncountable){
178 if(substr($lowercased_word,(-1*strlen($_uncountable))) == $_uncountable){
179 $_cached[$_original_word] = $word;
180 return $word;
184 foreach ($irregular as $_singular => $_plural){
185 if (preg_match('/('.$_plural.')$/i', $word, $arr)) {
186 $_cached[$_original_word] = preg_replace('/('.$_plural.')$/i', substr($arr[0],0,1).substr($_singular,1), $word);
187 return $_cached[$_original_word];
191 foreach ($singular as $rule => $replacement) {
192 if (preg_match($rule, $word)) {
193 $_cached[$_original_word] = preg_replace($rule, $replacement, $word);
194 return $_cached[$_original_word];
197 $_cached[$_original_word] = $word;
198 return $word;
200 return $_cached[$_original_word];
203 // }}}
204 // {{{ conditionalPlural()
207 * Get the plural form of a word if first parameter is greater than 1
209 * @param integer $numer_of_records
210 * @param string $word
211 * @return string Pluralized string when number of items is greater than 1
213 function conditionalPlural($numer_of_records, $word)
215 return $numer_of_records > 1 ? AkInflector::pluralize($word) : $word;
218 // }}}
219 // {{{ titleize()
222 * Converts an underscored or CamelCase word into a English
223 * sentence.
225 * The titleize function converts text like "WelcomePage",
226 * "welcome_page" or "welcome page" to this "Welcome
227 * Page".
228 * If second parameter is set to 'first' it will only
229 * capitalize the first character of the title.
231 * @access public
232 * @static
233 * @param string $word Word to format as tile
234 * @param string $uppercase If set to 'first' it will only uppercase the
235 * first character. Otherwise it will uppercase all
236 * the words in the title.
237 * @return string Text formatted as title
239 function titleize($word, $uppercase = '')
241 $uppercase = $uppercase == 'first' ? 'ucfirst' : 'ucwords';
242 return $uppercase(AkInflector::humanize(AkInflector::underscore($word)));
245 // }}}
246 // {{{ camelize()
249 * Returns given word as CamelCased
251 * Converts a word like "send_email" to "SendEmail". It
252 * will remove non alphanumeric character from the word, so
253 * "who's online" will be converted to "WhoSOnline"
255 * @access public
256 * @static
257 * @see variablize
258 * @param string $word Word to convert to camel case
259 * @return string UpperCamelCasedWord
261 function camelize($word)
263 static $_cached;
264 if(!isset($_cached[$word])){
265 if(preg_match_all('/\/(.?)/',$word,$got)){
266 foreach ($got[1] as $k=>$v){
267 $got[1][$k] = '::'.strtoupper($v);
269 $word = str_replace($got[0],$got[1],$word);
271 $_cached[$word] = str_replace(' ','',ucwords(preg_replace('/[^A-Z^a-z^0-9^:]+/',' ',$word)));
273 return $_cached[$word];
276 // }}}
277 // {{{ underscore()
280 * Converts a word "into_it_s_underscored_version"
282 * Convert any "CamelCased" or "ordinary Word" into an
283 * "underscored_word".
285 * This can be really useful for creating friendly URLs.
287 * @access public
288 * @static
289 * @param string $word Word to underscore
290 * @return string Underscored word
292 function underscore($word)
294 static $_cached;
295 if(!isset($_cached[$word])){
296 $_cached[$word] = strtolower(preg_replace(
297 array('/[^A-Z^a-z^0-9^\/]+/','/([a-z\d])([A-Z])/','/([A-Z]+)([A-Z][a-z])/'),
298 array('_','\1_\2','\1_\2'), $word));
300 return $_cached[$word];
304 // }}}
305 // {{{ humanize()
308 * Returns a human-readable string from $word
310 * Returns a human-readable string from $word, by replacing
311 * underscores with a space, and by upper-casing the initial
312 * character by default.
314 * If you need to uppercase all the words you just have to
315 * pass 'all' as a second parameter.
317 * @access public
318 * @static
319 * @param string $word String to "humanize"
320 * @param string $uppercase If set to 'all' it will uppercase all the words
321 * instead of just the first one.
322 * @return string Human-readable word
324 function humanize($word, $uppercase = '')
326 $uppercase = $uppercase == 'all' ? 'ucwords' : 'ucfirst';
327 return $uppercase(str_replace('_',' ',preg_replace('/_id$/', '',$word)));
330 // }}}
331 // {{{ variablize()
334 * Same as camelize but first char is lowercased
336 * Converts a word like "send_email" to "sendEmail". It
337 * will remove non alphanumeric character from the word, so
338 * "who's online" will be converted to "whoSOnline"
340 * @access public
341 * @static
342 * @see camelize
343 * @param string $word Word to lowerCamelCase
344 * @return string Returns a lowerCamelCasedWord
346 function variablize($word)
348 $word = AkInflector::camelize($word);
349 return strtolower($word[0]).substr($word,1);
352 // }}}
353 // {{{ tableize()
356 * Converts a class name to its table name according to rails
357 * naming conventions.
359 * Converts "Person" to "people"
361 * @access public
362 * @static
363 * @see classify
364 * @param string $class_name Class name for getting related table_name.
365 * @return string plural_table_name
367 function tableize($class_name)
369 return AkInflector::pluralize(AkInflector::underscore($class_name));
372 // }}}
373 // {{{ classify()
376 * Converts a table name to its class name according to Akelos
377 * naming conventions.
379 * Converts "people" to "Person"
381 * @access public
382 * @static
383 * @see tableize
384 * @param string $table_name Table name for getting related ClassName.
385 * @return string SingularClassName
387 function classify($table_name)
389 return AkInflector::camelize(AkInflector::singularize($table_name));
392 // }}}
393 // {{{ ordinalize()
396 * Converts number to its ordinal English form.
398 * This method converts 13 to 13th, 2 to 2nd ...
400 * @access public
401 * @static
402 * @param integer $number Number to get its ordinal value
403 * @return string Ordinal representation of given string.
405 function ordinalize($number)
407 if (in_array(($number % 100),range(11,13))){
408 return $number.'th';
409 }else{
410 switch (($number % 10)) {
411 case 1:
412 return $number.'st';
413 break;
414 case 2:
415 return $number.'nd';
416 break;
417 case 3:
418 return $number.'rd';
419 default:
420 return $number.'th';
421 break;
426 // }}}
429 * Removes the module name from a module/path, Module::name or Module_ControllerClassName.
431 * Example: AkInflector::demodulize('admin/dashboard_controller'); //=> dashboard_controller
432 * AkInflector::demodulize('Admin_DashboardController'); //=> DashboardController
433 * AkInflector::demodulize('Admin::Dashboard'); //=> Dashboard
435 function demodulize($module_name)
437 $module_name = str_replace('::', '/', $module_name);
438 return (strstr($module_name, '/') ? preg_replace('/^.*\//', '', $module_name) : (strstr($module_name, '_') ? substr($module_name, 1+strrpos($module_name,'_')) : $module_name));
442 * Transforms a string to its unaccented version.
443 * This might be useful for generating "friendly" URLs
445 function unaccent($text)
447 $map = array(
448 'À'=>'A', 'Á'=>'A', 'Â'=>'A', 'Ã'=>'A', 'Ä'=>'A', 'Å'=>'A', 'Æ'=>'A', 'Ç'=>'C',
449 'È'=>'E', 'É'=>'E', 'Ê'=>'E', 'Ë'=>'E', 'Ì'=>'I', 'Í'=>'I', 'Î'=>'I', 'Ï'=>'I',
450 'Ð'=>'D', 'Ñ'=>'N', 'Ò'=>'O', 'Ó'=>'O', 'Ô'=>'O', 'Õ'=>'O', 'Ö'=>'O', 'Ø'=>'O',
451 'Ù'=>'U', 'Ú'=>'U', 'Û'=>'U', 'Ü'=>'U', 'Ý'=>'Y', 'Þ'=>'T', 'ß'=>'s', 'à'=>'a',
452 'á'=>'a', 'â'=>'a', 'ã'=>'a', 'ä'=>'a', 'å'=>'a', 'æ'=>'a', 'ç'=>'c', 'è'=>'e',
453 'é'=>'e', 'ê'=>'e', 'ë'=>'e', 'ì'=>'i', 'í'=>'i', 'î'=>'i', 'ï'=>'i', 'ð'=>'e',
454 'ñ'=>'n', 'ò'=>'o', 'ó'=>'o', 'ô'=>'o', 'õ'=>'o', 'ö'=>'o', 'ø'=>'o', 'ù'=>'u',
455 'ú'=>'u', 'û'=>'u', 'ü'=>'u', 'ý'=>'y', 'þ'=>'t', 'ÿ'=>'y');
456 return str_replace(array_keys($map), array_values($map), $text);
460 function urlize($text)
462 return trim(AkInflector::underscore(AkInflector::unaccent($text)),'_');
466 * Returns $class_name in underscored form, with "_id" tacked on at the end.
467 * This is for use in dealing with the database.
469 * @param string $class_name
470 * @return string
472 function foreignKey($class_name, $separate_class_name_and_id_with_underscore = true)
474 return AkInflector::underscore(AkInflector::humanize(AkInflector::underscore($class_name))).($separate_class_name_and_id_with_underscore ? "_id" : "id");
477 function toControllerFilename($name)
479 $name = str_replace('::', '/', $name);
480 return AK_CONTROLLERS_DIR.DS.join(DS, array_map(array('AkInflector','underscore'), strstr($name, '/') ? explode('/', $name) : array($name))).'_controller.php';
483 function toModelFilename($name)
485 return AK_MODELS_DIR.DS.AkInflector::underscore($name).'.php';
488 function toHelperFilename($name)
490 return AK_APP_DIR.DS.'helpers'.DS.AkInflector::underscore($name).'.php';
493 function toFullName($name, $correct)
495 if (strstr($name, '_') && (strtolower($name) == $name)){
496 return AkInflector::camelize($name);
499 if (preg_match("/^(. * )({$correct})$/i", $name, $reg)){
500 if ($reg[2] == $correct){
501 return $name;
502 }else{
503 return ucfirst($reg[1].$correct);
505 }else{
506 return ucfirst($name.$correct);
510 function is_singular($singular)
512 return AkInflector::singularize(AkInflector::pluralize($singular)) == $singular;
515 function is_plural($plural)
517 return AkInflector::pluralize(AkInflector::singularize($plural)) == $plural;