463 lines
9.9 KiB
PHP
463 lines
9.9 KiB
PHP
|
<?php
|
||
|
/**
|
||
|
* @package Joomla.Platform
|
||
|
* @subpackage String
|
||
|
*
|
||
|
* @copyright Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved.
|
||
|
* @license GNU General Public License version 2 or later; see LICENSE
|
||
|
*/
|
||
|
|
||
|
defined('JPATH_PLATFORM') or die;
|
||
|
|
||
|
/**
|
||
|
* Joomla Platform String Inflector Class
|
||
|
*
|
||
|
* The Inflector transforms words
|
||
|
*
|
||
|
* @package Joomla.Platform
|
||
|
* @subpackage String
|
||
|
* @since 12.1
|
||
|
*/
|
||
|
class JStringInflector
|
||
|
{
|
||
|
/**
|
||
|
* The singleton instance.
|
||
|
*
|
||
|
* @var JStringInflector
|
||
|
* @since 12.1
|
||
|
*/
|
||
|
private static $_instance;
|
||
|
|
||
|
/**
|
||
|
* The inflector rules for singularisation, pluralisation and countability.
|
||
|
*
|
||
|
* @var array
|
||
|
* @since 12.1
|
||
|
*/
|
||
|
private $_rules = array(
|
||
|
'singular' => array(
|
||
|
'/(matr)ices$/i' => '\1ix',
|
||
|
'/(vert|ind)ices$/i' => '\1ex',
|
||
|
'/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
|
||
|
'/([ftw]ax)es/i' => '\1',
|
||
|
'/(cris|ax|test)es$/i' => '\1is',
|
||
|
'/(shoe|slave)s$/i' => '\1',
|
||
|
'/(o)es$/i' => '\1',
|
||
|
'/([^aeiouy]|qu)ies$/i' => '\1y',
|
||
|
'/$1ses$/i' => '\s',
|
||
|
'/ses$/i' => '\s',
|
||
|
'/eaus$/' => 'eau',
|
||
|
'/^(.*us)$/' => '\\1',
|
||
|
'/s$/i' => '',
|
||
|
),
|
||
|
'plural' => array(
|
||
|
'/([m|l])ouse$/i' => '\1ice',
|
||
|
'/(matr|vert|ind)(ix|ex)$/i' => '\1ices',
|
||
|
'/(x|ch|ss|sh)$/i' => '\1es',
|
||
|
'/([^aeiouy]|qu)y$/i' => '\1ies',
|
||
|
'/([^aeiouy]|qu)ies$/i' => '\1y',
|
||
|
'/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
|
||
|
'/sis$/i' => 'ses',
|
||
|
'/([ti])um$/i' => '\1a',
|
||
|
'/(buffal|tomat)o$/i' => '\1\2oes',
|
||
|
'/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i',
|
||
|
'/us$/i' => 'uses',
|
||
|
'/(ax|cris|test)is$/i' => '\1es',
|
||
|
'/s$/i' => 's',
|
||
|
'/$/' => 's',
|
||
|
),
|
||
|
'countable' => array(
|
||
|
'id',
|
||
|
'hits',
|
||
|
'clicks',
|
||
|
),
|
||
|
);
|
||
|
|
||
|
/**
|
||
|
* Cached inflections.
|
||
|
*
|
||
|
* The array is in the form [singular => plural]
|
||
|
*
|
||
|
* @var array
|
||
|
* @since 12.1
|
||
|
*/
|
||
|
private $_cache = array();
|
||
|
|
||
|
/**
|
||
|
* Protected constructor.
|
||
|
*
|
||
|
* @since 12.1
|
||
|
*/
|
||
|
protected function __construct()
|
||
|
{
|
||
|
// Pre=populate the irregual singular/plural.
|
||
|
$this
|
||
|
->addWord('deer')
|
||
|
->addWord('moose')
|
||
|
->addWord('sheep')
|
||
|
->addWord('bison')
|
||
|
->addWord('salmon')
|
||
|
->addWord('pike')
|
||
|
->addWord('trout')
|
||
|
->addWord('fish')
|
||
|
->addWord('swine')
|
||
|
|
||
|
->addWord('alias', 'aliases')
|
||
|
->addWord('bus', 'buses')
|
||
|
->addWord('foot', 'feet')
|
||
|
->addWord('goose', 'geese')
|
||
|
->addWord('hive', 'hives')
|
||
|
->addWord('louse', 'lice')
|
||
|
->addWord('man', 'men')
|
||
|
->addWord('mouse', 'mice')
|
||
|
->addWord('ox', 'oxen')
|
||
|
->addWord('quiz', 'quizes')
|
||
|
->addWord('status', 'statuses')
|
||
|
->addWord('tooth', 'teeth')
|
||
|
->addWord('woman', 'women');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Adds inflection regex rules to the inflector.
|
||
|
*
|
||
|
* @param mixed $data A string or an array of strings or regex rules to add.
|
||
|
* @param string $ruleType The rule type: singular | plural | countable
|
||
|
*
|
||
|
* @return void
|
||
|
*
|
||
|
* @since 12.1
|
||
|
* @throws InvalidArgumentException
|
||
|
*/
|
||
|
private function _addRule($data, $ruleType)
|
||
|
{
|
||
|
if (is_string($data))
|
||
|
{
|
||
|
$data = array($data);
|
||
|
}
|
||
|
elseif (!is_array($data))
|
||
|
{
|
||
|
// Do not translate.
|
||
|
throw new InvalidArgumentException('Invalid inflector rule data.');
|
||
|
}
|
||
|
|
||
|
foreach ($data as $rule)
|
||
|
{
|
||
|
// Ensure a string is pushed.
|
||
|
array_push($this->_rules[$ruleType], (string) $rule);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets an inflected word from the cache where the singular form is supplied.
|
||
|
*
|
||
|
* @param string $singular A singular form of a word.
|
||
|
*
|
||
|
* @return mixed The cached inflection or false if none found.
|
||
|
*
|
||
|
* @since 12.1
|
||
|
*/
|
||
|
private function _getCachedPlural($singular)
|
||
|
{
|
||
|
$singular = JString::strtolower($singular);
|
||
|
|
||
|
// Check if the word is in cache.
|
||
|
if (isset($this->_cache[$singular]))
|
||
|
{
|
||
|
return $this->_cache[$singular];
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets an inflected word from the cache where the plural form is supplied.
|
||
|
*
|
||
|
* @param string $plural A plural form of a word.
|
||
|
*
|
||
|
* @return mixed The cached inflection or false if none found.
|
||
|
*
|
||
|
* @since 12.1
|
||
|
*/
|
||
|
private function _getCachedSingular($plural)
|
||
|
{
|
||
|
$plural = JString::strtolower($plural);
|
||
|
|
||
|
return array_search($plural, $this->_cache);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Execute a regex from rules.
|
||
|
*
|
||
|
* The 'plural' rule type expects a singular word.
|
||
|
* The 'singular' rule type expects a plural word.
|
||
|
*
|
||
|
* @param string $word The string input.
|
||
|
* @param string $ruleType String (eg, singular|plural)
|
||
|
*
|
||
|
* @return mixed An inflected string, or false if no rule could be applied.
|
||
|
*
|
||
|
* @since 12.1
|
||
|
*/
|
||
|
private function _matchRegexRule($word, $ruleType)
|
||
|
{
|
||
|
// Cycle through the regex rules.
|
||
|
foreach ($this->_rules[$ruleType] as $regex => $replacement)
|
||
|
{
|
||
|
$matches = 0;
|
||
|
$matchedWord = preg_replace($regex, $replacement, $word, -1, $matches);
|
||
|
|
||
|
if ($matches > 0)
|
||
|
{
|
||
|
return $matchedWord;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets an inflected word in the cache.
|
||
|
*
|
||
|
* @param string $singular The singular form of the word.
|
||
|
* @param string $plural The plural form of the word. If omitted, it is assumed the singular and plural are identical.
|
||
|
*
|
||
|
* @return void
|
||
|
*
|
||
|
* @since 12.1
|
||
|
*/
|
||
|
private function _setCache($singular, $plural = null)
|
||
|
{
|
||
|
$singular = JString::strtolower($singular);
|
||
|
|
||
|
if ($plural === null)
|
||
|
{
|
||
|
$plural = $singular;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$plural = JString::strtolower($plural);
|
||
|
}
|
||
|
|
||
|
$this->_cache[$singular] = $plural;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Adds a countable word.
|
||
|
*
|
||
|
* @param mixed $data A string or an array of strings to add.
|
||
|
*
|
||
|
* @return JStringInflector Returns this object to support chaining.
|
||
|
*
|
||
|
* @since 12.1
|
||
|
*/
|
||
|
public function addCountableRule($data)
|
||
|
{
|
||
|
$this->_addRule($data, 'countable');
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Adds a specific singular-plural pair for a word.
|
||
|
*
|
||
|
* @param string $singular The singular form of the word.
|
||
|
* @param string $plural The plural form of the word. If omitted, it is assumed the singular and plural are identical.
|
||
|
*
|
||
|
* @return JStringInflector Returns this object to support chaining.
|
||
|
*
|
||
|
* @since 12.1
|
||
|
*/
|
||
|
public function addWord($singular, $plural =null)
|
||
|
{
|
||
|
$this->_setCache($singular, $plural);
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Adds a pluralisation rule.
|
||
|
*
|
||
|
* @param mixed $data A string or an array of regex rules to add.
|
||
|
*
|
||
|
* @return JStringInflector Returns this object to support chaining.
|
||
|
*
|
||
|
* @since 12.1
|
||
|
*/
|
||
|
public function addPluraliseRule($data)
|
||
|
{
|
||
|
$this->_addRule($data, 'plural');
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Adds a singularisation rule.
|
||
|
*
|
||
|
* @param mixed $data A string or an array of regex rules to add.
|
||
|
*
|
||
|
* @return JStringInflector Returns this object to support chaining.
|
||
|
*
|
||
|
* @since 12.1
|
||
|
*/
|
||
|
public function addSingulariseRule($data)
|
||
|
{
|
||
|
$this->_addRule($data, 'singular');
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets an instance of the JStringInflector singleton.
|
||
|
*
|
||
|
* @param boolean $new If true (default is false), returns a new instance regardless if one exists.
|
||
|
* This argument is mainly used for testing.
|
||
|
*
|
||
|
* @return JStringInflector
|
||
|
*
|
||
|
* @since 12.1
|
||
|
*/
|
||
|
public static function getInstance($new = false)
|
||
|
{
|
||
|
if ($new)
|
||
|
{
|
||
|
return new static;
|
||
|
}
|
||
|
elseif (!is_object(self::$_instance))
|
||
|
{
|
||
|
self::$_instance = new static;
|
||
|
}
|
||
|
|
||
|
return self::$_instance;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks if a word is countable.
|
||
|
*
|
||
|
* @param string $word The string input.
|
||
|
*
|
||
|
* @return boolean True if word is countable, false otherwise.
|
||
|
*
|
||
|
* @since 12.1
|
||
|
*/
|
||
|
public function isCountable($word)
|
||
|
{
|
||
|
return (boolean) in_array($word, $this->_rules['countable']);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks if a word is in a plural form.
|
||
|
*
|
||
|
* @param string $word The string input.
|
||
|
*
|
||
|
* @return boolean True if word is plural, false if not.
|
||
|
*
|
||
|
* @since 12.1
|
||
|
*/
|
||
|
public function isPlural($word)
|
||
|
{
|
||
|
// Try the cache for an known inflection.
|
||
|
$inflection = $this->_getCachedSingular($word);
|
||
|
|
||
|
if ($inflection !== false)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Compute the inflection to cache the values, and compare.
|
||
|
return $this->toPlural($this->toSingular($word)) == $word;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks if a word is in a singular form.
|
||
|
*
|
||
|
* @param string $word The string input.
|
||
|
*
|
||
|
* @return boolean True if word is singular, false if not.
|
||
|
*
|
||
|
* @since 12.1
|
||
|
*/
|
||
|
public function isSingular($word)
|
||
|
{
|
||
|
// Try the cache for an known inflection.
|
||
|
$inflection = $this->_getCachedPlural($word);
|
||
|
|
||
|
if ($inflection !== false)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Compute the inflection to cache the values, and compare.
|
||
|
return $this->toSingular($this->toPlural($word)) == $word;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Converts a word into its plural form.
|
||
|
*
|
||
|
* @param string $word The singular word to pluralise.
|
||
|
*
|
||
|
* @return mixed An inflected string, or false if no rule could be applied.
|
||
|
*
|
||
|
* @since 12.1
|
||
|
*/
|
||
|
public function toPlural($word)
|
||
|
{
|
||
|
// Try to get the cached plural form from the singular.
|
||
|
$cache = $this->_getCachedPlural($word);
|
||
|
if ($cache !== false)
|
||
|
{
|
||
|
return $cache;
|
||
|
}
|
||
|
|
||
|
// Check if the word is a known singular.
|
||
|
if ($this->_getCachedSingular($word))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Compute the inflection.
|
||
|
$inflected = $this->_matchRegexRule($word, 'plural');
|
||
|
if ($inflected !== false)
|
||
|
{
|
||
|
$this->_setCache($word, $inflected);
|
||
|
return $inflected;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Converts a word into its singular form.
|
||
|
*
|
||
|
* @param string $word The plural word to singularise.
|
||
|
*
|
||
|
* @return mixed An inflected string, or false if no rule could be applied.
|
||
|
*
|
||
|
* @since 12.1
|
||
|
*/
|
||
|
public function toSingular($word)
|
||
|
{
|
||
|
// Try to get the cached singular form from the plural.
|
||
|
$cache = $this->_getCachedSingular($word);
|
||
|
if ($cache !== false)
|
||
|
{
|
||
|
return $cache;
|
||
|
}
|
||
|
|
||
|
// Check if the word is a known plural.
|
||
|
if ($this->_getCachedPlural($word))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Compute the inflection.
|
||
|
$inflected = $this->_matchRegexRule($word, 'singular');
|
||
|
if ($inflected !== false)
|
||
|
{
|
||
|
$this->_setCache($inflected, $word);
|
||
|
return $inflected;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
}
|