From b95e99f86089c0f5f760a71baa397c8e08ba1df4 Mon Sep 17 00:00:00 2001 From: RA Date: Fri, 31 Jul 2015 22:19:21 +0300 Subject: [PATCH] =?UTF-8?q?feature=20namecase.=20=D0=A1=D0=BA=D0=BB=D0=BE?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=81=D0=BE=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D0=B2=D0=BD=D1=8B=D1=85=20=D1=84=D0=B0=D0=BC=D0=B8=D0=BB=D0=B8?= =?UTF-8?q?=D0=B9=20(=20=D0=A1=D0=B0=D0=BB=D1=82=D1=8B=D0=BA=D0=BE=D0=B2-?= =?UTF-8?q?=D0=A9=D0=B5=D0=B4=D1=80=D0=B8=D0=BD=20)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Library/NCL/NCLNameCaseCore.php | 2255 ++++++++++++++++--------------- Library/NCL/NCLNameCaseWord.php | 568 ++++---- 2 files changed, 1441 insertions(+), 1382 deletions(-) diff --git a/Library/NCL/NCLNameCaseCore.php b/Library/NCL/NCLNameCaseCore.php index 56666a1..3334305 100644 --- a/Library/NCL/NCLNameCaseCore.php +++ b/Library/NCL/NCLNameCaseCore.php @@ -5,11 +5,11 @@ * @package NameCaseLib */ /** - * + * */ if (!defined('NCL_DIR')) { - define('NCL_DIR', dirname(__FILE__)); + define('NCL_DIR', dirname(__FILE__)); } require_once NCL_DIR . '/NCL.php'; @@ -18,10 +18,10 @@ require_once NCL_DIR . '/NCLNameCaseWord.php'; /** * NCL NameCase Core - * + * * Набор основных функций, который позволяют сделать интерфейс слонения русского и украниского языка * абсолютно одинаковым. Содержит все функции для внешнего взаимодействия с библиотекой. - * + * * @author Андрей Чайка * @version 0.4.1 * @package NameCaseLib @@ -29,1108 +29,1151 @@ require_once NCL_DIR . '/NCLNameCaseWord.php'; class NCLNameCaseCore extends NCL { - /** - * Версия библиотеки - * @var string - */ - protected $version = '0.4.1'; - /** - * Версия языкового файла - * @var string - */ - protected $languageBuild = '0'; - /** - * Готовность системы: - * - Все слова идентифицированы (известо к какой части ФИО относится слово) - * - У всех слов определен пол - * Если все сделано стоит флаг true, при добавлении нового слова флаг сбрасывается на false - * @var bool - */ - private $ready = false; - /** - * Если все текущие слова было просклонены и в каждом слове уже есть результат склонения, - * тогда true. Если было добавлено новое слово флаг збрасывается на false - * @var bool - */ - private $finished = false; - /** - * Массив содержит елементы типа NCLNameCaseWord. Это все слова которые нужно обработать и просклонять - * @var array - */ - private $words = array(); - /** - * Переменная, в которую заносится слово с которым сейчас идет работа - * @var string - */ - protected $workingWord = ''; - /** - * Метод Last() вырезает подстроки разной длины. Посколько одинаковых вызовов бывает несколько, - * то все результаты выполнения кешируются в этом массиве. - * @var array - */ - protected $workindLastCache = array(); - /** - * Номер последнего использованого правила, устанавливается методом Rule() - * @var int - */ - private $lastRule = 0; - /** - * Массив содержит результат склонения слова - слово во всех падежах - * @var array - */ - protected $lastResult = array(); - /** - * Массив содержит информацию о том какие слова из массива $this->words относятся к - * фамилии, какие к отчеству а какие к имени. Массив нужен потому, что при добавлении слов мы не - * всегда знаем какая часть ФИО сейчас, поэтому после идентификации всех слов генерируется массив - * индексов для быстрого поиска в дальнейшем. - * @var array - */ - private $index = array(); - - /** - * Метод очищает результаты последнего склонения слова. Нужен при склонении нескольких слов. - */ - private function reset() - { - $this->lastRule = 0; - $this->lastResult = array(); - } - - /** - * Сбрасывает все информацию на начальную. Очищает все слова добавленые в систему. - * После выполнения система готова работать с начала. - * @return NCLNameCaseCore - */ - public function fullReset() - { - $this->words = array(); - $this->index = array('N' => array(), 'F' => array(), 'S' => array()); - $this->reset(); - $this->notReady(); - return $this; - } - - /** - * Устанавливает флаги о том, что система не готово и слова еще не были просклонены - */ - private function notReady() - { - $this->ready = false; - $this->finished = false; - } - - /** - * Устанавливает номер последнего правила - * @param int $index номер правила которое нужно установить - */ - protected function Rule($index) - { - $this->lastRule = $index; - } - - /** - * Устанавливает слово текущим для работы системы. Очищает кеш слова. - * @param string $word слово, которое нужно установить - */ - protected function setWorkingWord($word) - { - //Сбрасываем настройки - $this->reset(); - //Ставим слово - $this->workingWord = $word; - //Чистим кеш - $this->workindLastCache = array(); - } - - /** - * Если не нужно склонять слово, делает результат таким же как и именительный падеж - */ - protected function makeResultTheSame() - { - $this->lastResult = array_fill(0, $this->CaseCount, $this->workingWord); - } - - /** - * Если $stopAfter = 0, тогда вырезает $length последних букв с текущего слова ($this->workingWord) - * Если нет, тогда вырезает $stopAfter букв начиная от $length с конца - * @param int $length количество букв с конца - * @param int $stopAfter количество букв которые нужно вырезать (0 - все) - * @return string требуемая подстрока - */ - protected function Last($length=1, $stopAfter=0) - { - //Сколько букв нужно вырезать все или только часть - if (!$stopAfter) - { - $cut = $length; - } - else - { - $cut = $stopAfter; - } - - //Проверяем кеш - if (!isset($this->workindLastCache[$length][$stopAfter])) - { - $this->workindLastCache[$length][$stopAfter] = NCLStr::substr($this->workingWord, -$length, $cut); - } - return $this->workindLastCache[$length][$stopAfter]; - } - - /** - * Над текущим словом ($this->workingWord) выполняются правила в порядке указаном в $rulesArray. - * $gender служит для указания какие правила использовать мужские ('man') или женские ('woman') - * @param string $gender - префикс мужских/женских правил - * @param array $rulesArray - массив, порядок выполнения правил - * @return boolean если правило было задествовано, тогда true, если нет - тогда false - */ - protected function RulesChain($gender, $rulesArray) - { - foreach ($rulesArray as $ruleID) - { - $ruleMethod = $gender . 'Rule' . $ruleID; - if ($this->$ruleMethod()) - { - return true; - } - } - return false; - } - - /** - * Если $string строка, тогда проверяется входит ли буква $letter в строку $string - * Если $string массив, тогда проверяется входит ли строка $letter в массив $string - * @param string $letter буква или строка, которую нужно искать - * @param mixed $string строка или массив, в котором нужно искать - * @return bool true если искомое значение найдено - */ - protected function in($letter, $string) - { - //Если второй параметр массив - if (is_array($string)) - { - return in_array($letter, $string); - } - else - { - if (!$letter or NCLStr::strpos($string, $letter) === false) - { - return false; - } - else - { - return true; - } - } - } - - /** - * Функция проверяет, входит ли имя $nameNeedle в перечень имен $names. - * @param string $nameNeedle - имя которое нужно найти - * @param array $names - перечень имен в котором нужно найти имя - */ - protected function inNames($nameNeedle, $names) - { - if (!is_array($names)) - { - $names = array($names); - } - - foreach ($names as $name) - { - if (NCLStr::strtolower($nameNeedle) == NCLStr::strtolower($name)) - { - return true; - } - } - return false; - } - - /** - * Склоняет слово $word, удаляя из него $replaceLast последних букв - * и добавляя в каждый падеж окончание из массива $endings. - * @param string $word слово, к которому нужно добавить окончания - * @param array $endings массив окончаний - * @param int $replaceLast сколько последних букв нужно убрать с начального слова - */ - protected function wordForms($word, $endings, $replaceLast=0) - { - //Создаем массив с именительный падежом - $result = array($this->workingWord); - //Убираем в окончание лишние буквы - $word = NCLStr::substr($word, 0, NCLStr::strlen($word) - $replaceLast); - - //Добавляем окончания - for ($padegIndex = 1; $padegIndex < $this->CaseCount; $padegIndex++) - { - $result[$padegIndex] = $word . $endings[$padegIndex - 1]; - } - - $this->lastResult = $result; - } - - /** - * В массив $this->words добавляется новый об’єкт класса NCLNameCaseWord - * со словом $firstname и пометкой, что это имя - * @param string $firstname имя - * @return NCLNameCaseCore - */ - public function setFirstName($firstname="") - { - if ($firstname) - { - $index = count($this->words); - $this->words[$index] = new NCLNameCaseWord($firstname); - $this->words[$index]->setNamePart('N'); - $this->notReady(); - } - return $this; - } - - /** - * В массив $this->words добавляется новый об’єкт класса NCLNameCaseWord - * со словом $secondname и пометкой, что это фамилия - * @param string $secondname фамилия - * @return NCLNameCaseCore - */ - public function setSecondName($secondname="") - { - if ($secondname) - { - $index = count($this->words); - $this->words[$index] = new NCLNameCaseWord($secondname); - $this->words[$index]->setNamePart('S'); - $this->notReady(); - } - return $this; - } - - /** - * В массив $this->words добавляется новый об’єкт класса NCLNameCaseWord - * со словом $fathername и пометкой, что это отчество - * @param string $fathername отчество - * @return NCLNameCaseCore - */ - public function setFatherName($fathername="") - { - if ($fathername) - { - $index = count($this->words); - $this->words[$index] = new NCLNameCaseWord($fathername); - $this->words[$index]->setNamePart('F'); - $this->notReady(); - } - return $this; - } - - /** - * Всем словам устанавливается пол, который может иметь следующие значения - * - 0 - не определено - * - NCL::$MAN - мужчина - * - NCL::$WOMAN - женщина - * @param int $gender пол, который нужно установить - * @return NCLNameCaseCore - */ - public function setGender($gender=0) - { - foreach ($this->words as $word) - { - $word->setTrueGender($gender); - } - return $this; - } - - /** - * В система заносится сразу фамилия, имя, отчество - * @param string $secondName фамилия - * @param string $firstName имя - * @param string $fatherName отчество - * @return NCLNameCaseCore - */ - public function setFullName($secondName="", $firstName="", $fatherName="") - { - $this->setFirstName($firstName); - $this->setSecondName($secondName); - $this->setFatherName($fatherName); - return $this; - } - - /** - * В массив $this->words добавляется новый об’єкт класса NCLNameCaseWord - * со словом $firstname и пометкой, что это имя - * @param string $firstname имя - * @return NCLNameCaseCore - */ - public function setName($firstname="") - { - return $this->setFirstName($firstname); - } - - /** - * В массив $this->words добавляется новый об’єкт класса NCLNameCaseWord - * со словом $secondname и пометкой, что это фамилия - * @param string $secondname фамилия - * @return NCLNameCaseCore - */ - public function setLastName($secondname="") - { - return $this->setSecondName($secondname); - } - - /** - * В массив $this->words добавляется новый об’єкт класса NCLNameCaseWord - * со словом $secondname и пометкой, что это фамилия - * @param string $secondname фамилия - * @return NCLNameCaseCore - */ - public function setSirName($secondname="") - { - return $this->setSecondName($secondname); - } - - /** - * Если слово $word не идентифицировано, тогда определяется это имя, фамилия или отчество - * @param NCLNameCaseWord $word слово которое нужно идентифицировать - */ - private function prepareNamePart(NCLNameCaseWord $word) - { - if (!$word->getNamePart()) - { - $this->detectNamePart($word); - } - } - - /** - * Проверяет все ли слова идентифицированы, если нет тогда для каждого определяется это имя, фамилия или отчество - */ - private function prepareAllNameParts() - { - foreach ($this->words as $word) - { - $this->prepareNamePart($word); - } - } - - /** - * Определяет пол для слова $word - * @param NCLNameCaseWord $word слово для которого нужно определить пол - */ - private function prepareGender(NCLNameCaseWord $word) - { - if (!$word->isGenderSolved()) - { - $namePart = $word->getNamePart(); - switch ($namePart) - { - case 'N': $this->GenderByFirstName($word); - break; - case 'F': $this->GenderByFatherName($word); - break; - case 'S': $this->GenderBySecondName($word); - break; - } - } - } - - /** - * Для всех слов проверяет определен ли пол, если нет - определяет его - * После этого расчитывает пол для всех слов и устанавливает такой пол всем словам - * @return bool был ли определен пол - */ - private function solveGender() - { - //Ищем, может гдето пол уже установлен - foreach ($this->words as $word) - { - if ($word->isGenderSolved()) - { - $this->setGender($word->gender()); - return true; - } - } - - //Если нет тогда определяем у каждого слова и потом сумируем - $man = 0; - $woman = 0; - - foreach ($this->words as $word) - { - $this->prepareGender($word); - $gender = $word->getGender(); - $man+=$gender[NCL::$MAN]; - $woman+=$gender[NCL::$WOMAN]; - } - - if ($man > $woman) - { - $this->setGender(NCL::$MAN); - } - else - { - $this->setGender(NCL::$WOMAN); - } - - return true; - } - - /** - * Генерируется массив, который содержит информацию о том какие слова из массива $this->words относятся к - * фамилии, какие к отчеству а какие к имени. Массив нужен потому, что при добавлении слов мы не - * всегда знаем какая часть ФИО сейчас, поэтому после идентификации всех слов генерируется массив - * индексов для быстрого поиска в дальнейшем. - */ - private function generateIndex() - { - $this->index = array('N' => array(), 'S' => array(), 'F' => array()); - foreach ($this->words as $index => $word) - { - $namepart = $word->getNamePart(); - $this->index[$namepart][] = $index; - } - } - - /** - * Выполнет все необходимые подготовления для склонения. - * Все слова идентфицируются. Определяется пол. - * Обновляется индекс. - */ - private function prepareEverything() - { - if (!$this->ready) - { - $this->prepareAllNameParts(); - $this->solveGender(); - $this->generateIndex(); - $this->ready = true; - } - } - - /** - * По указаным словам определяется пол человека: - * - 0 - не определено - * - NCL::$MAN - мужчина - * - NCL::$WOMAN - женщина - * @return int текущий пол человека - */ - public function genderAutoDetect() - { - $this->prepareEverything(); - if (isset($this->words[0])) - { - return $this->words[0]->gender(); - } - return false; - } - - /** - * Разбивает строку $fullname на слова и возвращает формат в котором записано имя - * Формат: - * - S - Фамилия - * - N - Имя - * - F - Отчество - * @param string $fullname строка, для которой необходимо определить формат - * @return array формат в котором записано имя массив типа $this->words - */ - private function splitFullName($fullname) - { - - $fullname = trim($fullname); - $list = explode(' ', $fullname); - - foreach ($list as $word) - { - $this->words[] = new NCLNameCaseWord($word); - } - - $this->prepareEverything(); - $formatArr = array(); - - foreach ($this->words as $word) - { - $formatArr[] = $word->getNamePart(); - } - - return $this->words; - } - - /** - * Разбивает строку $fullname на слова и возвращает формат в котором записано имя - * Формат: - * - S - Фамилия - * - N - Имя - * - F - Отчество - * @param string $fullname строка, для которой необходимо определить формат - * @return string формат в котором записано имя - */ - public function getFullNameFormat($fullname) - { - $this->fullReset(); - $words = $this->splitFullName($fullname); - $format = ''; - foreach ($words as $word) - { - $format .= $word->getNamePart() . ' '; - } - return $format; - } - - /** - * Склоняет слово $word по нужным правилам в зависимости от пола и типа слова - * @param NCLNameCaseWord $word слово, которое нужно просклонять - */ - private function WordCase(NCLNameCaseWord $word) - { - $gender = ($word->gender() == NCL::$MAN ? 'man' : 'woman'); - - $namepart = ''; - - switch ($word->getNamePart()) - { - case 'F': $namepart = 'Father'; - break; - case 'N': $namepart = 'First'; - break; - case 'S': $namepart = 'Second'; - break; - } - - $method = $gender . $namepart . 'Name'; - - $this->setWorkingWord($word->getWord()); - - if ($this->$method()) - { - $word->setNameCases($this->lastResult); - $word->setRule($this->lastRule); - } - else - { - $word->setNameCases(array_fill(0, $this->CaseCount, $word->getWord())); - $word->setRule(-1); - } - } - - /** - * Производит склонение всех слов, который хранятся в массиве $this->words - */ - private function AllWordCases() - { - if (!$this->finished) - { - $this->prepareEverything(); - - foreach ($this->words as $word) - { - $this->WordCase($word); - } - - $this->finished = true; - } - } - - /** - * Если указан номер падежа $number, тогда возвращается строка с таким номером падежа, - * если нет, тогда возвращается массив со всеми падежами текущего слова. - * @param NCLNameCaseWord $word слово для котрого нужно вернуть падеж - * @param int $number номер падежа, который нужно вернуть - * @return mixed массив или строка с нужным падежом - */ - private function getWordCase(NCLNameCaseWord $word, $number=null) - { - $cases = $word->getNameCases(); - if (is_null($number) or $number < 0 or $number > ($this->CaseCount - 1)) - { - return $cases; - } - else - { - return $cases[$number]; - } - } - - /** - * Если нужно было просклонять несколько слов, то их необходимо собрать в одну строку. - * Эта функция собирает все слова указаные в $indexArray в одну строку. - * @param array $indexArray индексы слов, которые необходимо собрать вместе - * @param int $number номер падежа - * @return mixed либо массив со всеми падежами, либо строка с одним падежом - */ - private function getCasesConnected($indexArray, $number=null) - { - $readyArr = array(); - foreach ($indexArray as $index) - { - $readyArr[] = $this->getWordCase($this->words[$index], $number); - } - - $all = count($readyArr); - if ($all) - { - if (is_array($readyArr[0])) - { - //Масив нужно скелить каждый падеж - $resultArr = array(); - for ($case = 0; $case < $this->CaseCount; $case++) - { - $tmp = array(); - for ($i = 0; $i < $all; $i++) - { - $tmp[] = $readyArr[$i][$case]; - } - $resultArr[$case] = implode(' ', $tmp); - } - return $resultArr; - } - else - { - return implode(' ', $readyArr); - } - } - return ''; - } - - /** - * Функция ставит имя в нужный падеж. - * - * Если указан номер падежа $number, тогда возвращается строка с таким номером падежа, - * если нет, тогда возвращается массив со всеми падежами текущего слова. - * @param int $number номер падежа - * @return mixed массив или строка с нужным падежом - */ - public function getFirstNameCase($number=null) - { - $this->AllWordCases(); - - return $this->getCasesConnected($this->index['N'], $number); - } - - /** - * Функция ставит фамилию в нужный падеж. - * - * Если указан номер падежа $number, тогда возвращается строка с таким номером падежа, - * если нет, тогда возвращается массив со всеми падежами текущего слова. - * @param int $number номер падежа - * @return mixed массив или строка с нужным падежом - */ - public function getSecondNameCase($number=null) - { - $this->AllWordCases(); - - return $this->getCasesConnected($this->index['S'], $number); - } - - /** - * Функция ставит отчество в нужный падеж. - * - * Если указан номер падежа $number, тогда возвращается строка с таким номером падежа, - * если нет, тогда возвращается массив со всеми падежами текущего слова. - * @param int $number номер падежа - * @return mixed массив или строка с нужным падежом - */ - public function getFatherNameCase($number=null) - { - $this->AllWordCases(); - - return $this->getCasesConnected($this->index['F'], $number); - } - - /** - * Функция ставит имя $firstName в нужный падеж $CaseNumber по правилам пола $gender. - * - * Если указан номер падежа $CaseNumber, тогда возвращается строка с таким номером падежа, - * если нет, тогда возвращается массив со всеми падежами текущего слова. - * @param string $firstName имя, которое нужно просклонять - * @param int $CaseNumber номер падежа - * @param int $gender пол, который нужно использовать - * @return mixed массив или строка с нужным падежом - */ - public function qFirstName($firstName, $CaseNumber=null, $gender=0) - { - $this->fullReset(); - $this->setFirstName($firstName); - if ($gender) - { - $this->setGender($gender); - } - return $this->getFirstNameCase($CaseNumber); - } - - /** - * Функция ставит фамилию $secondName в нужный падеж $CaseNumber по правилам пола $gender. - * - * Если указан номер падежа $CaseNumber, тогда возвращается строка с таким номером падежа, - * если нет, тогда возвращается массив со всеми падежами текущего слова. - * @param string $secondName фамилия, которую нужно просклонять - * @param int $CaseNumber номер падежа - * @param int $gender пол, который нужно использовать - * @return mixed массив или строка с нужным падежом - */ - public function qSecondName($secondName, $CaseNumber=null, $gender=0) - { - $this->fullReset(); - $this->setSecondName($secondName); - if ($gender) - { - $this->setGender($gender); - } - - return $this->getSecondNameCase($CaseNumber); - } - - /** - * Функция ставит отчество $fatherName в нужный падеж $CaseNumber по правилам пола $gender. - * - * Если указан номер падежа $CaseNumber, тогда возвращается строка с таким номером падежа, - * если нет, тогда возвращается массив со всеми падежами текущего слова. - * @param string $fatherName отчество, которое нужно просклонять - * @param int $CaseNumber номер падежа - * @param int $gender пол, который нужно использовать - * @return mixed массив или строка с нужным падежом - */ - public function qFatherName($fatherName, $CaseNumber=null, $gender=0) - { - $this->fullReset(); - $this->setFatherName($fatherName); - if ($gender) - { - $this->setGender($gender); - } - return $this->getFatherNameCase($CaseNumber); - } - - /** - * Склоняет текущие слова во все падежи и форматирует слово по шаблону $format - * Формат: - * - S - Фамилия - * - N - Имя - * - F - Отчество - * @param string $format строка формат - * @return array массив со всеми падежами - */ - public function getFormattedArray($format) - { - if (is_array($format)) - { - return $this->getFormattedArrayHard($format); - } - - $length = NCLStr::strlen($format); - $result = array(); - $cases = array(); - $cases['S'] = $this->getCasesConnected($this->index['S']); - $cases['N'] = $this->getCasesConnected($this->index['N']); - $cases['F'] = $this->getCasesConnected($this->index['F']); - - for ($curCase = 0; $curCase < $this->CaseCount; $curCase++) - { - $line = ""; - for ($i = 0; $i < $length; $i++) - { - $symbol = NCLStr::substr($format, $i, 1); - if ($symbol == 'S') - { - $line.=$cases['S'][$curCase]; - } - elseif ($symbol == 'N') - { - $line.=$cases['N'][$curCase]; - } - elseif ($symbol == 'F') - { - $line.=$cases['F'][$curCase]; - } - else - { - $line.=$symbol; - } - } - $result[] = $line; - } - return $result; - } - - /** - * Склоняет текущие слова во все падежи и форматирует слово по шаблону $format - * Формат: - * - S - Фамилия - * - N - Имя - * - F - Отчество - * @param array $format массив с форматом - * @return array массив со всеми падежами - */ - public function getFormattedArrayHard($format) - { - - $result = array(); - $cases = array(); - foreach ($format as $word) - { - $cases[] = $word->getNameCases(); - } - - for ($curCase = 0; $curCase < $this->CaseCount; $curCase++) - { - $line = ""; - foreach ($cases as $value) - { - $line.=$value[$curCase] . ' '; - } - $result[] = trim($line); - } - return $result; - } - - /** - * Склоняет текущие слова в падеж $caseNum и форматирует слово по шаблону $format - * Формат: - * - S - Фамилия - * - N - Имя - * - F - Отчество - * @param array $format массив с форматом - * @return string строка в нужном падеже - */ - public function getFormattedHard($caseNum=0, $format=array()) - { - $result = ""; - foreach ($format as $word) - { - $cases = $word->getNameCases(); - $result.= $cases[$caseNum] . ' '; - } - return trim($result); - } - - /** - * Склоняет текущие слова в падеж $caseNum и форматирует слово по шаблону $format - * Формат: - * - S - Фамилия - * - N - Имя - * - F - Отчество - * @param string $format строка с форматом - * @return string строка в нужном падеже - */ - public function getFormatted($caseNum=0, $format="S N F") - { - $this->AllWordCases(); - //Если не указан падеж используем другую функцию - if (is_null($caseNum) or !$caseNum) - { - return $this->getFormattedArray($format); - } - //Если формат сложный - elseif (is_array($format)) - { - return $this->getFormattedHard($caseNum, $format); - } - else - { - $length = NCLStr::strlen($format); - $result = ""; - for ($i = 0; $i < $length; $i++) - { - $symbol = NCLStr::substr($format, $i, 1); - if ($symbol == 'S') - { - $result.=$this->getSecondNameCase($caseNum); - } - elseif ($symbol == 'N') - { - $result.=$this->getFirstNameCase($caseNum); - } - elseif ($symbol == 'F') - { - $result.=$this->getFatherNameCase($caseNum); - } - else - { - $result.=$symbol; - } - } - return $result; - } - } - - /** - * Склоняет фамилию $secondName, имя $firstName, отчество $fatherName - * в падеж $caseNum по правилам пола $gender и форматирует результат по шаблону $format - * Формат: - * - S - Фамилия - * - N - Имя - * - F - Отчество - * @param string $secondName фамилия - * @param string $firstName имя - * @param string $fatherName отчество - * @param int $gender пол - * @param int $caseNum номер падежа - * @param string $format формат - * @return mixed либо массив со всеми падежами, либо строка - */ - public function qFullName($secondName="", $firstName="", $fatherName="", $gender=0, $caseNum=0, $format="S N F") - { - $this->fullReset(); - $this->setFirstName($firstName); - $this->setSecondName($secondName); - $this->setFatherName($fatherName); - if ($gender) - { - $this->setGender($gender); - } - - return $this->getFormatted($caseNum, $format); - } - - /** - * Склоняет ФИО $fullname в падеж $caseNum по правилам пола $gender. - * Возвращает результат в таком же формате, как он и был. - * @param string $fullname ФИО - * @param int $caseNum номер падежа - * @param int $gender пол человека - * @return mixed либо массив со всеми падежами, либо строка - */ - public function q($fullname, $caseNum=null, $gender=null) - { - $this->fullReset(); - $format = $this->splitFullName($fullname); - if ($gender) - { - $this->setGender($gender); - } - - return $this->getFormatted($caseNum, $format); - } - - /** - * Определяет пол человека по ФИО - * @param string $fullname ФИО - * @return int пол человека - */ - public function genderDetect($fullname) - { - $this->fullReset(); - $this->splitFullName($fullname); - return $this->genderAutoDetect(); - } - - /** - * Возвращает внутренний массив $this->words каждая запись имеет тип NCLNameCaseWord - * @return array Массив всех слов в системе - */ - public function getWordsArray() - { - return $this->words; - } - - /** - * Функция пытается применить цепочку правил для мужских имен - * @return boolean true - если было использовано правило из списка, false - если правило не было найденым - */ - protected function manFirstName() - { - return false; - } - - /** - * Функция пытается применить цепочку правил для женских имен - * @return boolean true - если было использовано правило из списка, false - если правило не было найденым - */ - protected function womanFirstName() - { - return false; - } - - /** - * Функция пытается применить цепочку правил для мужских фамилий - * @return boolean true - если было использовано правило из списка, false - если правило не было найденым - */ - protected function manSecondName() - { - return false; - } - - /** - * Функция пытается применить цепочку правил для женских фамилий - * @return boolean true - если было использовано правило из списка, false - если правило не было найденым - */ - protected function womanSecondName() - { - return false; - } - - /** - * Функция склоняет мужский отчества - * @return boolean true - если слово было успешно изменено, false - если не получилось этого сделать - */ - protected function manFatherName() - { - return false; - } - - /** - * Функция склоняет женские отчества - * @return boolean true - если слово было успешно изменено, false - если не получилось этого сделать - */ - protected function womanFatherName() - { - return false; - } - - /** - * Определение пола по правилам имен - * @param NCLNameCaseWord $word обьект класса слов, для которого нужно определить пол - */ - protected function GenderByFirstName(NCLNameCaseWord $word) - { - - } - - /** - * Определение пола по правилам фамилий - * @param NCLNameCaseWord $word обьект класса слов, для которого нужно определить пол - */ - protected function GenderBySecondName(NCLNameCaseWord $word) - { - - } - - /** - * Определение пола по правилам отчеств - * @param NCLNameCaseWord $word обьект класса слов, для которого нужно определить пол - */ - protected function GenderByFatherName(NCLNameCaseWord $word) - { - - } - - /** - * Идетифицирует слово определяе имя это, или фамилия, или отчество - * - N - имя - * - S - фамилия - * - F - отчество - * @param NCLNameCaseWord $word обьект класса слов, который необходимо идентифицировать - */ - protected function detectNamePart(NCLNameCaseWord $word) - { - - } - - /** - * Возвращает версию библиотеки - * @return string версия библиотеки - */ - public function version() - { - return $this->version; - } - - /** - * Возвращает версию использованого языкового файла - * @return string версия языкового файла - */ - public function languageVersion() - { - return $this->languageBuild; - } + /** + * Версия библиотеки + * @var string + */ + protected $version = '0.4.1'; + /** + * Версия языкового файла + * @var string + */ + protected $languageBuild = '0'; + /** + * Готовность системы: + * - Все слова идентифицированы (известо к какой части ФИО относится слово) + * - У всех слов определен пол + * Если все сделано стоит флаг true, при добавлении нового слова флаг сбрасывается на false + * @var bool + */ + private $ready = false; + /** + * Если все текущие слова было просклонены и в каждом слове уже есть результат склонения, + * тогда true. Если было добавлено новое слово флаг збрасывается на false + * @var bool + */ + private $finished = false; + /** + * Массив содержит елементы типа NCLNameCaseWord. Это все слова которые нужно обработать и просклонять + * @var array + */ + private $words = array(); + /** + * Переменная, в которую заносится слово с которым сейчас идет работа + * @var string + */ + protected $workingWord = ''; + /** + * Метод Last() вырезает подстроки разной длины. Посколько одинаковых вызовов бывает несколько, + * то все результаты выполнения кешируются в этом массиве. + * @var array + */ + protected $workindLastCache = array(); + /** + * Номер последнего использованого правила, устанавливается методом Rule() + * @var int + */ + private $lastRule = 0; + /** + * Массив содержит результат склонения слова - слово во всех падежах + * @var array + */ + protected $lastResult = array(); + /** + * Массив содержит информацию о том какие слова из массива $this->words относятся к + * фамилии, какие к отчеству а какие к имени. Массив нужен потому, что при добавлении слов мы не + * всегда знаем какая часть ФИО сейчас, поэтому после идентификации всех слов генерируется массив + * индексов для быстрого поиска в дальнейшем. + * @var array + */ + private $index = array(); + + /** + * Метод очищает результаты последнего склонения слова. Нужен при склонении нескольких слов. + */ + private function reset() + { + $this->lastRule = 0; + $this->lastResult = array(); + } + + /** + * Сбрасывает все информацию на начальную. Очищает все слова добавленые в систему. + * После выполнения система готова работать с начала. + * @return NCLNameCaseCore + */ + public function fullReset() + { + $this->words = array(); + $this->index = array('N' => array(), 'F' => array(), 'S' => array()); + $this->reset(); + $this->notReady(); + return $this; + } + + /** + * Устанавливает флаги о том, что система не готово и слова еще не были просклонены + */ + private function notReady() + { + $this->ready = false; + $this->finished = false; + } + + /** + * Устанавливает номер последнего правила + * @param int $index номер правила которое нужно установить + */ + protected function Rule($index) + { + $this->lastRule = $index; + } + + /** + * Устанавливает слово текущим для работы системы. Очищает кеш слова. + * @param string $word слово, которое нужно установить + */ + protected function setWorkingWord($word) + { + //Сбрасываем настройки + $this->reset(); + //Ставим слово + $this->workingWord = $word; + //Чистим кеш + $this->workindLastCache = array(); + } + + /** + * Если не нужно склонять слово, делает результат таким же как и именительный падеж + */ + protected function makeResultTheSame() + { + $this->lastResult = array_fill(0, $this->CaseCount, $this->workingWord); + } + + /** + * Если $stopAfter = 0, тогда вырезает $length последних букв с текущего слова ($this->workingWord) + * Если нет, тогда вырезает $stopAfter букв начиная от $length с конца + * @param int $length количество букв с конца + * @param int $stopAfter количество букв которые нужно вырезать (0 - все) + * @return string требуемая подстрока + */ + protected function Last($length=1, $stopAfter=0) + { + //Сколько букв нужно вырезать все или только часть + if (!$stopAfter) + { + $cut = $length; + } + else + { + $cut = $stopAfter; + } + + //Проверяем кеш + if (!isset($this->workindLastCache[$length][$stopAfter])) + { + $this->workindLastCache[$length][$stopAfter] = NCLStr::substr($this->workingWord, -$length, $cut); + } + return $this->workindLastCache[$length][$stopAfter]; + } + + /** + * Над текущим словом ($this->workingWord) выполняются правила в порядке указаном в $rulesArray. + * $gender служит для указания какие правила использовать мужские ('man') или женские ('woman') + * @param string $gender - префикс мужских/женских правил + * @param array $rulesArray - массив, порядок выполнения правил + * @return boolean если правило было задествовано, тогда true, если нет - тогда false + */ + protected function RulesChain($gender, $rulesArray) + { + foreach ($rulesArray as $ruleID) + { + $ruleMethod = $gender . 'Rule' . $ruleID; + if ($this->$ruleMethod()) + { + return true; + } + } + return false; + } + + /** + * Если $string строка, тогда проверяется входит ли буква $letter в строку $string + * Если $string массив, тогда проверяется входит ли строка $letter в массив $string + * @param string $letter буква или строка, которую нужно искать + * @param mixed $string строка или массив, в котором нужно искать + * @return bool true если искомое значение найдено + */ + protected function in($letter, $string) + { + //Если второй параметр массив + if (is_array($string)) + { + return in_array($letter, $string); + } + else + { + if (!$letter or NCLStr::strpos($string, $letter) === false) + { + return false; + } + else + { + return true; + } + } + } + + /** + * Функция проверяет, входит ли имя $nameNeedle в перечень имен $names. + * @param string $nameNeedle - имя которое нужно найти + * @param array $names - перечень имен в котором нужно найти имя + */ + protected function inNames($nameNeedle, $names) + { + if (!is_array($names)) + { + $names = array($names); + } + + foreach ($names as $name) + { + if (NCLStr::strtolower($nameNeedle) == NCLStr::strtolower($name)) + { + return true; + } + } + return false; + } + + /** + * Склоняет слово $word, удаляя из него $replaceLast последних букв + * и добавляя в каждый падеж окончание из массива $endings. + * @param string $word слово, к которому нужно добавить окончания + * @param array $endings массив окончаний + * @param int $replaceLast сколько последних букв нужно убрать с начального слова + */ + protected function wordForms($word, $endings, $replaceLast=0) + { + //Создаем массив с именительный падежом + $result = array($this->workingWord); + //Убираем в окончание лишние буквы + $word = NCLStr::substr($word, 0, NCLStr::strlen($word) - $replaceLast); + + //Добавляем окончания + for ($padegIndex = 1; $padegIndex < $this->CaseCount; $padegIndex++) + { + $result[$padegIndex] = $word . $endings[$padegIndex - 1]; + } + + $this->lastResult = $result; + } + + /** + * В массив $this->words добавляется новый об’єкт класса NCLNameCaseWord + * со словом $firstname и пометкой, что это имя + * @param string $firstname имя + * @return NCLNameCaseCore + */ + public function setFirstName($firstname="") + { + if ($firstname) + { + $index = count($this->words); + $this->words[$index] = new NCLNameCaseWord($firstname); + $this->words[$index]->setNamePart('N'); + $this->notReady(); + } + return $this; + } + + /** + * В массив $this->words добавляется новый об’єкт класса NCLNameCaseWord + * со словом $secondname и пометкой, что это фамилия + * @param string $secondname фамилия + * @return NCLNameCaseCore + */ + public function setSecondName($secondname="") + { + if ($secondname) + { + $index = count($this->words); + $this->words[$index] = new NCLNameCaseWord($secondname); + $this->words[$index]->setNamePart('S'); + $this->notReady(); + } + return $this; + } + + /** + * В массив $this->words добавляется новый об’єкт класса NCLNameCaseWord + * со словом $fathername и пометкой, что это отчество + * @param string $fathername отчество + * @return NCLNameCaseCore + */ + public function setFatherName($fathername="") + { + if ($fathername) + { + $index = count($this->words); + $this->words[$index] = new NCLNameCaseWord($fathername); + $this->words[$index]->setNamePart('F'); + $this->notReady(); + } + return $this; + } + + /** + * Всем словам устанавливается пол, который может иметь следующие значения + * - 0 - не определено + * - NCL::$MAN - мужчина + * - NCL::$WOMAN - женщина + * @param int $gender пол, который нужно установить + * @return NCLNameCaseCore + */ + public function setGender($gender=0) + { + foreach ($this->words as $word) + { + $word->setTrueGender($gender); + } + return $this; + } + + /** + * В система заносится сразу фамилия, имя, отчество + * @param string $secondName фамилия + * @param string $firstName имя + * @param string $fatherName отчество + * @return NCLNameCaseCore + */ + public function setFullName($secondName="", $firstName="", $fatherName="") + { + $this->setFirstName($firstName); + $this->setSecondName($secondName); + $this->setFatherName($fatherName); + return $this; + } + + /** + * В массив $this->words добавляется новый об’єкт класса NCLNameCaseWord + * со словом $firstname и пометкой, что это имя + * @param string $firstname имя + * @return NCLNameCaseCore + */ + public function setName($firstname="") + { + return $this->setFirstName($firstname); + } + + /** + * В массив $this->words добавляется новый об’єкт класса NCLNameCaseWord + * со словом $secondname и пометкой, что это фамилия + * @param string $secondname фамилия + * @return NCLNameCaseCore + */ + public function setLastName($secondname="") + { + return $this->setSecondName($secondname); + } + + /** + * В массив $this->words добавляется новый об’єкт класса NCLNameCaseWord + * со словом $secondname и пометкой, что это фамилия + * @param string $secondname фамилия + * @return NCLNameCaseCore + */ + public function setSirName($secondname="") + { + return $this->setSecondName($secondname); + } + + /** + * Если слово $word не идентифицировано, тогда определяется это имя, фамилия или отчество + * @param NCLNameCaseWord $word слово которое нужно идентифицировать + */ + private function prepareNamePart(NCLNameCaseWord $word) + { + if (!$word->getNamePart()) + { + $this->detectNamePart($word); + } + } + + /** + * Проверяет все ли слова идентифицированы, если нет тогда для каждого определяется это имя, фамилия или отчество + */ + private function prepareAllNameParts() + { + foreach ($this->words as $word) + { + $this->prepareNamePart($word); + } + } + + /** + * Определяет пол для слова $word + * @param NCLNameCaseWord $word слово для которого нужно определить пол + */ + private function prepareGender(NCLNameCaseWord $word) + { + if (!$word->isGenderSolved()) + { + $namePart = $word->getNamePart(); + switch ($namePart) + { + case 'N': $this->GenderByFirstName($word); + break; + case 'F': $this->GenderByFatherName($word); + break; + case 'S': $this->GenderBySecondName($word); + break; + } + } + } + + /** + * Для всех слов проверяет определен ли пол, если нет - определяет его + * После этого расчитывает пол для всех слов и устанавливает такой пол всем словам + * @return bool был ли определен пол + */ + private function solveGender() + { + //Ищем, может гдето пол уже установлен + foreach ($this->words as $word) + { + if ($word->isGenderSolved()) + { + $this->setGender($word->gender()); + return true; + } + } + + //Если нет тогда определяем у каждого слова и потом сумируем + $man = 0; + $woman = 0; + + foreach ($this->words as $word) + { + $this->prepareGender($word); + $gender = $word->getGender(); + $man+=$gender[NCL::$MAN]; + $woman+=$gender[NCL::$WOMAN]; + } + + if ($man > $woman) + { + $this->setGender(NCL::$MAN); + } + else + { + $this->setGender(NCL::$WOMAN); + } + + return true; + } + + /** + * Генерируется массив, который содержит информацию о том какие слова из массива $this->words относятся к + * фамилии, какие к отчеству а какие к имени. Массив нужен потому, что при добавлении слов мы не + * всегда знаем какая часть ФИО сейчас, поэтому после идентификации всех слов генерируется массив + * индексов для быстрого поиска в дальнейшем. + */ + private function generateIndex() + { + $this->index = array('N' => array(), 'S' => array(), 'F' => array()); + foreach ($this->words as $index => $word) + { + $namepart = $word->getNamePart(); + $this->index[$namepart][] = $index; + } + } + + /** + * Выполнет все необходимые подготовления для склонения. + * Все слова идентфицируются. Определяется пол. + * Обновляется индекс. + */ + private function prepareEverything() + { + if (!$this->ready) + { + $this->prepareAllNameParts(); + $this->solveGender(); + $this->generateIndex(); + $this->ready = true; + } + } + + /** + * По указаным словам определяется пол человека: + * - 0 - не определено + * - NCL::$MAN - мужчина + * - NCL::$WOMAN - женщина + * @return int текущий пол человека + */ + public function genderAutoDetect() + { + $this->prepareEverything(); + if (isset($this->words[0])) + { + return $this->words[0]->gender(); + } + return false; + } + + /** + * Разбивает строку $fullname на слова и возвращает формат в котором записано имя + * Формат: + * - S - Фамилия + * - N - Имя + * - F - Отчество + * @param string $fullname строка, для которой необходимо определить формат + * @return array формат в котором записано имя массив типа $this->words + */ + private function splitFullName($fullname) + { + + $fullname = trim($fullname); + $list = explode(' ', $fullname); + + foreach ($list as $word) + { + $this->words[] = new NCLNameCaseWord($word); + } + + $this->prepareEverything(); + $formatArr = array(); + + foreach ($this->words as $word) + { + $formatArr[] = $word->getNamePart(); + } + + return $this->words; + } + + /** + * Разбивает строку $fullname на слова и возвращает формат в котором записано имя + * Формат: + * - S - Фамилия + * - N - Имя + * - F - Отчество + * @param string $fullname строка, для которой необходимо определить формат + * @return string формат в котором записано имя + */ + public function getFullNameFormat($fullname) + { + $this->fullReset(); + $words = $this->splitFullName($fullname); + $format = ''; + foreach ($words as $word) + { + $format .= $word->getNamePart() . ' '; + } + return $format; + } + + /** + * Склоняет слово $word по нужным правилам в зависимости от пола и типа слова + * @param NCLNameCaseWord $word слово, которое нужно просклонять + */ + private function WordCase(NCLNameCaseWord $word) + { + $gender = ($word->gender() == NCL::$MAN ? 'man' : 'woman'); + + $namepart = ''; + + $name_part_letter=$word->getNamePart(); + switch ($name_part_letter) + { + case 'F': $namepart = 'Father'; + break; + case 'N': $namepart = 'First'; + break; + case 'S': $namepart = 'Second'; + break; + } + + $method = $gender . $namepart . 'Name'; + + //если фамилия из 2х слов через дефис + //http://new.gramota.ru/spravka/buro/search-answer?s=273912 + + //рабоиваем слово с дефисами на части + $tmp=$word->getWordOrig(); + $cur_words=explode('-', $tmp); + $o_cur_words=array(); + + $result=array(); + $last_rule=-1; + + $cnt=count($cur_words); + foreach ($cur_words as $k=>$cur_word){ + $is_norm_rules=true; + $o_ncw=new NCLNameCaseWord($cur_word); + if ( $name_part_letter=='S' && $cnt>1 && $k<$cnt-1 ){ + //если первая часть фамилии тоже фамилия, то склоняем по общим правилам + //иначе не склоняется + $o_nc = new NCLNameCaseRu(); + $o_nc->detectNamePart( $o_ncw ); + $is_norm_rules=( $o_ncw->getNamePart()==$name_part_letter ); + } + + $this->setWorkingWord($cur_word); + + if ($is_norm_rules && $this->$method()) + { + //склоняется + $result_tmp=$this->lastResult; + $last_rule=$this->lastRule; + } + else + { + //не склоняется. Заполняем что есть + $result_tmp=array_fill(0, $this->CaseCount, $cur_word); + $last_rule=-1; + } + + $o_ncw->setNameCases($result_tmp); + $o_cur_words[]=$o_ncw; + } + + //объединение пачку частей слова в одно слово по каждому падежу + foreach ($o_cur_words as $o_ncw){ + $namecases=$o_ncw->getNameCases(); + foreach ($namecases as $k=>$namecase){ + if ( key_exists($k, $result) ) $result[$k]=$result[$k].'-'.$namecase; + else $result[$k]=$namecase; + } + } + + //устанавливаем падежи для целого слова + $word->setNameCases($result, false); + $word->setRule($last_rule); + } + + /** + * Производит склонение всех слов, который хранятся в массиве $this->words + */ + private function AllWordCases() + { + if (!$this->finished) + { + $this->prepareEverything(); + + foreach ($this->words as $word) + { + $this->WordCase($word); + } + + $this->finished = true; + } + } + + /** + * Если указан номер падежа $number, тогда возвращается строка с таким номером падежа, + * если нет, тогда возвращается массив со всеми падежами текущего слова. + * @param NCLNameCaseWord $word слово для котрого нужно вернуть падеж + * @param int $number номер падежа, который нужно вернуть + * @return mixed массив или строка с нужным падежом + */ + private function getWordCase(NCLNameCaseWord $word, $number=null) + { + $cases = $word->getNameCases(); + if (is_null($number) or $number < 0 or $number > ($this->CaseCount - 1)) + { + return $cases; + } + else + { + return $cases[$number]; + } + } + + /** + * Если нужно было просклонять несколько слов, то их необходимо собрать в одну строку. + * Эта функция собирает все слова указаные в $indexArray в одну строку. + * @param array $indexArray индексы слов, которые необходимо собрать вместе + * @param int $number номер падежа + * @return mixed либо массив со всеми падежами, либо строка с одним падежом + */ + private function getCasesConnected($indexArray, $number=null) + { + $readyArr = array(); + foreach ($indexArray as $index) + { + $readyArr[] = $this->getWordCase($this->words[$index], $number); + } + + $all = count($readyArr); + if ($all) + { + if (is_array($readyArr[0])) + { + //Масив нужно скелить каждый падеж + $resultArr = array(); + for ($case = 0; $case < $this->CaseCount; $case++) + { + $tmp = array(); + for ($i = 0; $i < $all; $i++) + { + $tmp[] = $readyArr[$i][$case]; + } + $resultArr[$case] = implode(' ', $tmp); + } + return $resultArr; + } + else + { + return implode(' ', $readyArr); + } + } + return ''; + } + + /** + * Функция ставит имя в нужный падеж. + * + * Если указан номер падежа $number, тогда возвращается строка с таким номером падежа, + * если нет, тогда возвращается массив со всеми падежами текущего слова. + * @param int $number номер падежа + * @return mixed массив или строка с нужным падежом + */ + public function getFirstNameCase($number=null) + { + $this->AllWordCases(); + + return $this->getCasesConnected($this->index['N'], $number); + } + + /** + * Функция ставит фамилию в нужный падеж. + * + * Если указан номер падежа $number, тогда возвращается строка с таким номером падежа, + * если нет, тогда возвращается массив со всеми падежами текущего слова. + * @param int $number номер падежа + * @return mixed массив или строка с нужным падежом + */ + public function getSecondNameCase($number=null) + { + $this->AllWordCases(); + + return $this->getCasesConnected($this->index['S'], $number); + } + + /** + * Функция ставит отчество в нужный падеж. + * + * Если указан номер падежа $number, тогда возвращается строка с таким номером падежа, + * если нет, тогда возвращается массив со всеми падежами текущего слова. + * @param int $number номер падежа + * @return mixed массив или строка с нужным падежом + */ + public function getFatherNameCase($number=null) + { + $this->AllWordCases(); + + return $this->getCasesConnected($this->index['F'], $number); + } + + /** + * Функция ставит имя $firstName в нужный падеж $CaseNumber по правилам пола $gender. + * + * Если указан номер падежа $CaseNumber, тогда возвращается строка с таким номером падежа, + * если нет, тогда возвращается массив со всеми падежами текущего слова. + * @param string $firstName имя, которое нужно просклонять + * @param int $CaseNumber номер падежа + * @param int $gender пол, который нужно использовать + * @return mixed массив или строка с нужным падежом + */ + public function qFirstName($firstName, $CaseNumber=null, $gender=0) + { + $this->fullReset(); + $this->setFirstName($firstName); + if ($gender) + { + $this->setGender($gender); + } + return $this->getFirstNameCase($CaseNumber); + } + + /** + * Функция ставит фамилию $secondName в нужный падеж $CaseNumber по правилам пола $gender. + * + * Если указан номер падежа $CaseNumber, тогда возвращается строка с таким номером падежа, + * если нет, тогда возвращается массив со всеми падежами текущего слова. + * @param string $secondName фамилия, которую нужно просклонять + * @param int $CaseNumber номер падежа + * @param int $gender пол, который нужно использовать + * @return mixed массив или строка с нужным падежом + */ + public function qSecondName($secondName, $CaseNumber=null, $gender=0) + { + $this->fullReset(); + $this->setSecondName($secondName); + if ($gender) + { + $this->setGender($gender); + } + + return $this->getSecondNameCase($CaseNumber); + } + + /** + * Функция ставит отчество $fatherName в нужный падеж $CaseNumber по правилам пола $gender. + * + * Если указан номер падежа $CaseNumber, тогда возвращается строка с таким номером падежа, + * если нет, тогда возвращается массив со всеми падежами текущего слова. + * @param string $fatherName отчество, которое нужно просклонять + * @param int $CaseNumber номер падежа + * @param int $gender пол, который нужно использовать + * @return mixed массив или строка с нужным падежом + */ + public function qFatherName($fatherName, $CaseNumber=null, $gender=0) + { + $this->fullReset(); + $this->setFatherName($fatherName); + if ($gender) + { + $this->setGender($gender); + } + return $this->getFatherNameCase($CaseNumber); + } + + /** + * Склоняет текущие слова во все падежи и форматирует слово по шаблону $format + * Формат: + * - S - Фамилия + * - N - Имя + * - F - Отчество + * @param string $format строка формат + * @return array массив со всеми падежами + */ + public function getFormattedArray($format) + { + if (is_array($format)) + { + return $this->getFormattedArrayHard($format); + } + + $length = NCLStr::strlen($format); + $result = array(); + $cases = array(); + $cases['S'] = $this->getCasesConnected($this->index['S']); + $cases['N'] = $this->getCasesConnected($this->index['N']); + $cases['F'] = $this->getCasesConnected($this->index['F']); + + for ($curCase = 0; $curCase < $this->CaseCount; $curCase++) + { + $line = ""; + for ($i = 0; $i < $length; $i++) + { + $symbol = NCLStr::substr($format, $i, 1); + if ($symbol == 'S') + { + $line.=$cases['S'][$curCase]; + } + elseif ($symbol == 'N') + { + $line.=$cases['N'][$curCase]; + } + elseif ($symbol == 'F') + { + $line.=$cases['F'][$curCase]; + } + else + { + $line.=$symbol; + } + } + $result[] = $line; + } + return $result; + } + + /** + * Склоняет текущие слова во все падежи и форматирует слово по шаблону $format + * Формат: + * - S - Фамилия + * - N - Имя + * - F - Отчество + * @param array $format массив с форматом + * @return array массив со всеми падежами + */ + public function getFormattedArrayHard($format) + { + + $result = array(); + $cases = array(); + foreach ($format as $word) + { + $cases[] = $word->getNameCases(); + } + + for ($curCase = 0; $curCase < $this->CaseCount; $curCase++) + { + $line = ""; + foreach ($cases as $value) + { + $line.=$value[$curCase] . ' '; + } + $result[] = trim($line); + } + return $result; + } + + /** + * Склоняет текущие слова в падеж $caseNum и форматирует слово по шаблону $format + * Формат: + * - S - Фамилия + * - N - Имя + * - F - Отчество + * @param array $format массив с форматом + * @return string строка в нужном падеже + */ + public function getFormattedHard($caseNum=0, $format=array()) + { + $result = ""; + foreach ($format as $word) + { + $cases = $word->getNameCases(); + $result.= $cases[$caseNum] . ' '; + } + return trim($result); + } + + /** + * Склоняет текущие слова в падеж $caseNum и форматирует слово по шаблону $format + * Формат: + * - S - Фамилия + * - N - Имя + * - F - Отчество + * @param string $format строка с форматом + * @return string строка в нужном падеже + */ + public function getFormatted($caseNum=0, $format="S N F") + { + $this->AllWordCases(); + //Если не указан падеж используем другую функцию + if (is_null($caseNum) or !$caseNum) + { + return $this->getFormattedArray($format); + } + //Если формат сложный + elseif (is_array($format)) + { + return $this->getFormattedHard($caseNum, $format); + } + else + { + $length = NCLStr::strlen($format); + $result = ""; + for ($i = 0; $i < $length; $i++) + { + $symbol = NCLStr::substr($format, $i, 1); + if ($symbol == 'S') + { + $result.=$this->getSecondNameCase($caseNum); + } + elseif ($symbol == 'N') + { + $result.=$this->getFirstNameCase($caseNum); + } + elseif ($symbol == 'F') + { + $result.=$this->getFatherNameCase($caseNum); + } + else + { + $result.=$symbol; + } + } + return $result; + } + } + + /** + * Склоняет фамилию $secondName, имя $firstName, отчество $fatherName + * в падеж $caseNum по правилам пола $gender и форматирует результат по шаблону $format + * Формат: + * - S - Фамилия + * - N - Имя + * - F - Отчество + * @param string $secondName фамилия + * @param string $firstName имя + * @param string $fatherName отчество + * @param int $gender пол + * @param int $caseNum номер падежа + * @param string $format формат + * @return mixed либо массив со всеми падежами, либо строка + */ + public function qFullName($secondName="", $firstName="", $fatherName="", $gender=0, $caseNum=0, $format="S N F") + { + $this->fullReset(); + $this->setFirstName($firstName); + $this->setSecondName($secondName); + $this->setFatherName($fatherName); + if ($gender) + { + $this->setGender($gender); + } + + return $this->getFormatted($caseNum, $format); + } + + /** + * Склоняет ФИО $fullname в падеж $caseNum по правилам пола $gender. + * Возвращает результат в таком же формате, как он и был. + * @param string $fullname ФИО + * @param int $caseNum номер падежа + * @param int $gender пол человека + * @return mixed либо массив со всеми падежами, либо строка + */ + public function q($fullname, $caseNum=null, $gender=null) + { + $this->fullReset(); + $format = $this->splitFullName($fullname); + if ($gender) + { + $this->setGender($gender); + } + + return $this->getFormatted($caseNum, $format); + } + + /** + * Определяет пол человека по ФИО + * @param string $fullname ФИО + * @return int пол человека + */ + public function genderDetect($fullname) + { + $this->fullReset(); + $this->splitFullName($fullname); + return $this->genderAutoDetect(); + } + + /** + * Возвращает внутренний массив $this->words каждая запись имеет тип NCLNameCaseWord + * @return array Массив всех слов в системе + */ + public function getWordsArray() + { + return $this->words; + } + + /** + * Функция пытается применить цепочку правил для мужских имен + * @return boolean true - если было использовано правило из списка, false - если правило не было найденым + */ + protected function manFirstName() + { + return false; + } + + /** + * Функция пытается применить цепочку правил для женских имен + * @return boolean true - если было использовано правило из списка, false - если правило не было найденым + */ + protected function womanFirstName() + { + return false; + } + + /** + * Функция пытается применить цепочку правил для мужских фамилий + * @return boolean true - если было использовано правило из списка, false - если правило не было найденым + */ + protected function manSecondName() + { + return false; + } + + /** + * Функция пытается применить цепочку правил для женских фамилий + * @return boolean true - если было использовано правило из списка, false - если правило не было найденым + */ + protected function womanSecondName() + { + return false; + } + + /** + * Функция склоняет мужский отчества + * @return boolean true - если слово было успешно изменено, false - если не получилось этого сделать + */ + protected function manFatherName() + { + return false; + } + + /** + * Функция склоняет женские отчества + * @return boolean true - если слово было успешно изменено, false - если не получилось этого сделать + */ + protected function womanFatherName() + { + return false; + } + + /** + * Определение пола по правилам имен + * @param NCLNameCaseWord $word обьект класса слов, для которого нужно определить пол + */ + protected function GenderByFirstName(NCLNameCaseWord $word) + { + + } + + /** + * Определение пола по правилам фамилий + * @param NCLNameCaseWord $word обьект класса слов, для которого нужно определить пол + */ + protected function GenderBySecondName(NCLNameCaseWord $word) + { + + } + + /** + * Определение пола по правилам отчеств + * @param NCLNameCaseWord $word обьект класса слов, для которого нужно определить пол + */ + protected function GenderByFatherName(NCLNameCaseWord $word) + { + + } + + /** + * Идетифицирует слово определяе имя это, или фамилия, или отчество + * - N - имя + * - S - фамилия + * - F - отчество + * @param NCLNameCaseWord $word обьект класса слов, который необходимо идентифицировать + */ + protected function detectNamePart(NCLNameCaseWord $word) + { + + } + + /** + * Возвращает версию библиотеки + * @return string версия библиотеки + */ + public function version() + { + return $this->version; + } + + /** + * Возвращает версию использованого языкового файла + * @return string версия языкового файла + */ + public function languageVersion() + { + return $this->languageBuild; + } } diff --git a/Library/NCL/NCLNameCaseWord.php b/Library/NCL/NCLNameCaseWord.php index e7c1019..c004a23 100644 --- a/Library/NCL/NCLNameCaseWord.php +++ b/Library/NCL/NCLNameCaseWord.php @@ -6,7 +6,7 @@ /** * NCLNameCaseWord - класс, который служит для хранения всей информации о каждом слове - * + * * @author Андрей Чайка * @version 0.4.1 * @package NameCaseLib @@ -14,282 +14,298 @@ class NCLNameCaseWord { - /** - * Слово в нижнем регистре, которое хранится в об’єкте класса - * @var string - */ - private $word = ''; - - /** - * Тип текущей записи (Фамилия/Имя/Отчество) - * - N - ім’я - * - S - прізвище - * - F - по-батькові - * @var string - */ - private $namePart = null; - - /** - * Вероятность того, что текущей слово относится к мужскому полу - * @var int - */ - private $genderMan = 0; - - /** - * Вероятность того, что текущей слово относится к женскому полу - * @var int - */ - private $genderWoman = 0; - - /** - * Окончательное решение, к какому полу относится слово - * - 0 - не определено - * - NCL::$MAN - мужской пол - * - NCL::$WOMAN - женский пол - * @var int - */ - private $genderSolved = 0; - - /** - * Маска больших букв в слове. - * - * Содержит информацию о том, какие буквы в слове были большими, а какие мальникими: - * - x - маленькая буква - * - X - больная буква - * @var array - */ - private $letterMask = array(); - - /** - * Содержит true, если все слово было в верхнем регистре и false, если не было - * @var bool - */ - private $isUpperCase = false; - - /** - * Массив содержит все падежи слова, полученые после склонения текущего слова - * @var array - */ - private $NameCases = array(); - - /** - * Номер правила, по которому было произведено склонение текущего слова - * @var int - */ - private $rule = 0; - - /** - * Создание нового обьекта со словом $word - * @param string $word слово - */ - public function __construct($word) - { - $this->generateMask($word); - $this->word = NCLStr::strtolower($word); - } - - /** - * Генерирует маску, которая содержит информацию о том, какие буквы в слове были большими, а какие маленькими: - * - x - маленькая буква - * - X - больная буква - * @param string $word слово, для которого генерировать маску - */ - private function generateMask($word) - { - $letters = NCLStr::splitLetters($word); - $mask = array(); - $this->isUpperCase = true; - foreach ($letters as $letter) - { - if (NCLStr::isLowerCase($letter)) - { - $mask[] = 'x'; - $this->isUpperCase = false; - } - else - { - $mask[] = 'X'; - } - } - $this->letterMask = $mask; - } - - /** - * Возвращает все падежи слова в начальную маску: - * - x - маленькая буква - * - X - больная буква - */ - private function returnMask() - { - if ($this->isUpperCase) - { - foreach ($this->NameCases as $index => $case) - { - $this->NameCases[$index] = NCLStr::strtoupper($this->NameCases[$index]); - } - } - else - { - $splitedMask = $this->letterMask; - $maskLength = count($splitedMask); - foreach ($this->NameCases as $index => $case) - { - $caseLength = NCLStr::strlen($case); + /** + * Слово в нижнем регистре, которое хранится в об’єкте класса + * @var string + */ + private $word = ''; - $max = min(array($caseLength, $maskLength)); - $this->NameCases[$index] = ''; - for ($letterIndex = 0; $letterIndex < $max; $letterIndex++) - { - $letter = NCLStr::substr($case, $letterIndex, 1); - if ($splitedMask[$letterIndex] == 'X') - { - $letter = NCLStr::strtoupper($letter); - } - $this->NameCases[$index] .= $letter; - } - $this->NameCases[$index] .= NCLStr::substr($case, $max, $caseLength-$maskLength); - } - } - } - - /** - * Сохраняет результат склонения текущего слова - * @param array $nameCases массив со всеми падежами - */ - public function setNameCases($nameCases) - { - $this->NameCases = $nameCases; - $this->returnMask(); - } - - /** - * Возвращает массив со всеми падежами текущего слова - * @return array массив со всеми падежами - */ - public function getNameCases() - { - return $this->NameCases; - } - - /** - * Возвращает строку с нужным падежом текущего слова - * @param int $number нужный падеж - * @return string строка с нужным падежом текущего слова - */ - public function getNameCase($number) - { - if(isset($this->NameCases[$number])) - { - return $this->NameCases[$number]; - } - return false; - } - - /** - * Расчитывает и возвращает пол текущего слова - * @return int пол текущего слова - */ - public function gender() - { - if (!$this->genderSolved) - { - if ($this->genderMan > $this->genderWoman) - { - $this->genderSolved = NCL::$MAN; - } - else - { - $this->genderSolved = NCL::$WOMAN; - } - } - return $this->genderSolved; - } - - /** - * Устанавливает вероятности того, что даное слово является мужчиной или женщиной - * @param int $man вероятность того, что слово мужчина - * @param int $woman верятность того, что слово женщина - */ - public function setGender($man, $woman) - { - $this->genderMan = $man; - $this->genderWoman = $woman; - } - - /** - * Окончательно устанавливает пол человека - * - 0 - не определено - * - NCL::$MAN - мужчина - * - NCL::$WOMAN - женщина - * @param int $gender пол человека - */ - public function setTrueGender($gender) - { - $this->genderSolved = $gender; - } - - /** - * Возвращает массив вероятности того, что даное слово является мужчиной или женщиной - * @return array массив вероятностей - */ - public function getGender() - { - return array(NCL::$MAN => $this->genderMan, NCL::$WOMAN => $this->genderWoman); - } - - /** - * Устанавливает тип текущего слова - * Тип слова: - * - S - Фамилия - * - N - Имя - * - F - Отчество - * @param string $namePart тип слова - */ - public function setNamePart($namePart) - { - $this->namePart = $namePart; - } - - /** - * Возвращает тип текущего слова - * Тип слова: - * - S - Фамилия - * - N - Имя - * - F - Отчество - * @return string $namePart тип слова - */ - public function getNamePart() - { - return $this->namePart; - } - - /** - * Возвращает текущее слово. - * @return string текущее слово - */ - public function getWord() - { - return $this->word; - } - - /** - * Если уже был расчитан пол для всех слов системы, тогда каждому слову предается окончательное - * решение. Эта функция определяет было ли принято окончательное решение. - * @return bool было ли принято окончательное решение по поводу пола текущего слова - */ - public function isGenderSolved() - { - return ($this->genderSolved ? true : false); - } - - /** - * Устанавливает номер правила по которому склонялось текущее слово. - * @param int $ruleID номер правила - */ - public function setRule($ruleID) - { - $this->rule = $ruleID; - } + /** + * Оригинальное слово + * @var string + */ + private $word_orig = ''; + + /** + * Тип текущей записи (Фамилия/Имя/Отчество) + * - N - ім’я + * - S - прізвище + * - F - по-батькові + * @var string + */ + private $namePart = null; + + /** + * Вероятность того, что текущей слово относится к мужскому полу + * @var int + */ + private $genderMan = 0; + + /** + * Вероятность того, что текущей слово относится к женскому полу + * @var int + */ + private $genderWoman = 0; + + /** + * Окончательное решение, к какому полу относится слово + * - 0 - не определено + * - NCL::$MAN - мужской пол + * - NCL::$WOMAN - женский пол + * @var int + */ + private $genderSolved = 0; + + /** + * Маска больших букв в слове. + * + * Содержит информацию о том, какие буквы в слове были большими, а какие мальникими: + * - x - маленькая буква + * - X - больная буква + * @var array + */ + private $letterMask = array(); + + /** + * Содержит true, если все слово было в верхнем регистре и false, если не было + * @var bool + */ + private $isUpperCase = false; + + /** + * Массив содержит все падежи слова, полученые после склонения текущего слова + * @var array + */ + private $NameCases = array(); + + /** + * Номер правила, по которому было произведено склонение текущего слова + * @var int + */ + private $rule = 0; + + /** + * Создание нового обьекта со словом $word + * @param string $word слово + */ + public function __construct($word) + { + $this->word_orig=$word; + $this->generateMask($word); + $this->word = NCLStr::strtolower($word); + } + + /** + * Генерирует маску, которая содержит информацию о том, какие буквы в слове были большими, а какие маленькими: + * - x - маленькая буква + * - X - больная буква + * @param string $word слово, для которого генерировать маску + */ + private function generateMask($word) + { + $letters = NCLStr::splitLetters($word); + $mask = array(); + $this->isUpperCase = true; + foreach ($letters as $letter) + { + if (NCLStr::isLowerCase($letter)) + { + $mask[] = 'x'; + $this->isUpperCase = false; + } + else + { + $mask[] = 'X'; + } + } + $this->letterMask = $mask; + } + + /** + * Возвращает все падежи слова в начальную маску: + * - x - маленькая буква + * - X - больная буква + */ + private function returnMask() + { + if ($this->isUpperCase) + { + foreach ($this->NameCases as $index => $case) + { + $this->NameCases[$index] = NCLStr::strtoupper($this->NameCases[$index]); + } + } + else + { + $splitedMask = $this->letterMask; + $maskLength = count($splitedMask); + foreach ($this->NameCases as $index => $case) + { + $caseLength = NCLStr::strlen($case); + + $max = min(array($caseLength, $maskLength)); + $this->NameCases[$index] = ''; + for ($letterIndex = 0; $letterIndex < $max; $letterIndex++) + { + $letter = NCLStr::substr($case, $letterIndex, 1); + if ($splitedMask[$letterIndex] == 'X') + { + $letter = NCLStr::strtoupper($letter); + } + $this->NameCases[$index] .= $letter; + } + $this->NameCases[$index] .= NCLStr::substr($case, $max, $caseLength-$maskLength); + } + } + } + + /** + * Сохраняет результат склонения текущего слова + * @param array $nameCases массив со всеми падежами + */ + public function setNameCases($nameCases, $is_return_mask=true) + { + $this->NameCases = $nameCases; + if ($is_return_mask) $this->returnMask(); + } + + /** + * Возвращает массив со всеми падежами текущего слова + * @return array массив со всеми падежами + */ + public function getNameCases() + { + return $this->NameCases; + } + + /** + * Возвращает строку с нужным падежом текущего слова + * @param int $number нужный падеж + * @return string строка с нужным падежом текущего слова + */ + public function getNameCase($number) + { + if(isset($this->NameCases[$number])) + { + return $this->NameCases[$number]; + } + return false; + } + + /** + * Расчитывает и возвращает пол текущего слова + * @return int пол текущего слова + */ + public function gender() + { + if (!$this->genderSolved) + { + if ($this->genderMan > $this->genderWoman) + { + $this->genderSolved = NCL::$MAN; + } + else + { + $this->genderSolved = NCL::$WOMAN; + } + } + return $this->genderSolved; + } + + /** + * Устанавливает вероятности того, что даное слово является мужчиной или женщиной + * @param int $man вероятность того, что слово мужчина + * @param int $woman верятность того, что слово женщина + */ + public function setGender($man, $woman) + { + $this->genderMan = $man; + $this->genderWoman = $woman; + } + + /** + * Окончательно устанавливает пол человека + * - 0 - не определено + * - NCL::$MAN - мужчина + * - NCL::$WOMAN - женщина + * @param int $gender пол человека + */ + public function setTrueGender($gender) + { + $this->genderSolved = $gender; + } + + /** + * Возвращает массив вероятности того, что даное слово является мужчиной или женщиной + * @return array массив вероятностей + */ + public function getGender() + { + return array(NCL::$MAN => $this->genderMan, NCL::$WOMAN => $this->genderWoman); + } + + /** + * Устанавливает тип текущего слова + * Тип слова: + * - S - Фамилия + * - N - Имя + * - F - Отчество + * @param string $namePart тип слова + */ + public function setNamePart($namePart) + { + $this->namePart = $namePart; + } + + /** + * Возвращает тип текущего слова + * Тип слова: + * - S - Фамилия + * - N - Имя + * - F - Отчество + * @return string $namePart тип слова + */ + public function getNamePart() + { + return $this->namePart; + } + + /** + * Возвращает текущее слово. + * @return string текущее слово + */ + public function getWord() + { + return $this->word; + } + + /** + * Возвращает текущее оригинальное слово. + * @return string текущее слово + */ + public function getWordOrig() + { + return $this->word_orig; + } + + /** + * Если уже был расчитан пол для всех слов системы, тогда каждому слову предается окончательное + * решение. Эта функция определяет было ли принято окончательное решение. + * @return bool было ли принято окончательное решение по поводу пола текущего слова + */ + public function isGenderSolved() + { + return ($this->genderSolved ? true : false); + } + + /** + * Устанавливает номер правила по которому склонялось текущее слово. + * @param int $ruleID номер правила + */ + public function setRule($ruleID) + { + $this->rule = $ruleID; + } } ?>