Функция склонения имён

Не так давно, я разговаривал о программировании, вёрстке и сайтостроении с одним знакомым, как всегда упомянул свой блог, и тут он мне говорит, что на моём блоге о PHP «ни слуху, ни духу». Честно, меня это задело. А ведь и правда, статей о скриптах на PHP, совсем нет. Поэтому я решил, что в ближайшее время, обязательно напишу какой-нибудь скрипт.
Поэтому я решил написать вот эту функцию, по склонению имён с указанием пола и падежа. Решил я назвать её smart_name(). Получилось немножко громоздко, но… В функции имеется 12 массивов, мде… многовато, но всё-таки. 6 массивов для женских имён и 6 массивов для мужских. Первый массив — именительный падеж, второй — родительный, третий — дательный, четвёртый — винительный, пятый — творительный, шестой — предложный. Хочу заметить, что в массивах хранятся окончания имён. Имя, окончание которого отсутствует в базе, склоняться не будет, например мужское имя Вилли, или женское Лили. Также учтена особеность склонения имён с одинаковым окончанием -ия-, Анастасия и Лия будут склоняться по-разному. В общем хватит лишних слов, вот код функции:

class SmartName {

    private $_name,
            $_encode,
            $_error,
            $_endings = array(
                array( // женские
                   array('ла','ка','ша','ра','ня','тя','на','га','ля','та','да','са','ия','дя','ся','ья','вь','ва','оя','за','ая','ма','фа','йа'),
                   array('лы','ки','ши','ры','ни','ти','ны','ги','ли','ты','ды','сы','ии','ди','си','ьи','ви','вы','ои','зы','аи','мы','фы','йи'),
                   array('ле','ке','ше','ре','не','те','не','ге','ле','те','де','се','ии','де','се','ье','ви','ве','ое','зе','ае','ме','фе','йе'),
                   array('лу','ку','шу','ру','ню','тю','ну','гу','лю','ту','ду','су','ию','дю','сю','ью','вь','ву','ою','зу','аю','му','фу','йю'),
                   array('лой','кой','шей','рой','ней','тей','ной','гой','лей','той','дой','сой','ией','дей','сей','ьей','вью','вой','оей','зой','аей','мой','фой','йей'),
                   array('ле','ке','ше','ре','не','те','не','ге','ле','те','де','се','ии','де','се','ье','ви','ве','ое','зе','ае','ме','фе','йе')
                ),
                array( // мужские
                    array('ма','ас','ша','ей','ня','ик','ва','рь','ля','лл','им','кс','тя','ад','ан','ат','ий','ха','ём','ем','ян','ис','ай','ир','ав','эн','ен','ег','ил','еб','ев','ам','он','ид','рп','ин','ор','ст','от','иф','кс','ар','та','нт','рх','тр','ум','ов','рк','ьф','ед','ьд','кт','ьм','яс','их','ет','ия','ья','ак','рт','рл'),
                    array('мы','аса','ши','ея','ни','ика','вы','ря','ли','лла','има','кса','ти','ада','ана','ата','ия','хи','ёма','ема','яна','иса','ая','ира','ава','эна','ена','ега','ила','еба','ьва','ама','она','ида','рпа','ина','ора','ста','ота','ифа','кса','ара','ты','нта','рха','тра','ума','ова','рка','ьфа','еда','ьда','кта','ьма','яса','иха','ета','ия','ьи','ака','рта','рла'),
                    array('ме','асу','ше','ею','не','ику','ве','рю','ле','ллу','иму','ксу','те','аду','ану','ату','ию','хе','ёму','ему','яну','ису','аю','иру','аву','эну','ену','егу','илу','ебу','ьву','аму','ону','иду','рпу','ину','ору','сту','оту','ифу','ксу','ару','те','нту','рху','тру','уму','ову','рку','ьфу','еду','ьду','кту','ьму','ясу','иху','ету','ию','ье','аку','рту','рлу'),
                    array('му','аса','шу','ея','ню','ика','ву','ря','лю','лла','има','кса','тю','ада','ана','ата','ия','ху','ёму','ему','яна','иса','ая','ира','ава','эна','ена','ега','ила','еба','ьва','ама','она','ида','рпа','ина','ора','ста','ота','ифа','кса','ара','ту','нта','рха','тра','ума','ова','рка','ьфа','еда','ьда','кта','ьма','яса','иха','ета','ия','ью','ака','рта','рла'),
                    array('мой','аcом','шей','еем','ней','иком','вой','рем','лей','ллом','има','ксом','тей','адом','аном','атом','ием','хой','ёмом','емом','яном','исом','аем','иром','авом','эном','еном','егом','илом','ебом','ьвом','амом','оном','идом','рпом','ином','ором','стом','отом','ифом','ксом','аром','той','нтом','рхом','тром','умом','овом','рком','ьфом','едом','ьдом','ктом','ьмом','ясом','ихом','етом','ием','ьёй','аком','ртом','рлом'),
                    array('ме','асе','ше','ее','не','ике','ве','ре','ле','лле','име','ксе','те','аде','ане','ате','ии','хе','ёме','еме','яне','исе','ае','ире','аве','эне','ене','еге','иле','ебе','ьве','аме','оне','иде','рпе','ине','оре','сте','оте','ифе','ксе','аре','те','нте','рхе','тре','уме','ове','рке','ьфе','еде','ьде','кте','ьме','ясе','ихе','ете','ие','ье','аке','рте','рле')
                )
            );

    public function __construct($name)
    {
        $this->_encode = mb_detect_encoding($name);
        if($this->_encode != 'UTF-8') {
            $name = utf8_encode($name);
        }
        $this->_name = strtolower(trim($name));
    }

    /**
     *
     * @param int $case Падеж (0 - Именительный, 1 - Родительный, 2 - Дательный, 3 - Винительный, 4 - Творительынй, 5 - Предложный)
     * @param type $gender Пол (0 - женский, 1 - мужской)
     */
    public function dispose($case, $gender) {
        $this->_error = null;
        $end = substr($this->_name, -4);
        $const = str_replace($end, '', $this->_name);

        $result = null;

        if ($case > 0 && $case < 6) {
            if ($gender == 0 || $gender == 1) {
                $key = array_search($end, $this->_endings[$gender][0]);
                if ($key) {
                    $result = $const . $this->_endings[$gender][$case][$key];
                } else {
                    $this->_error = "Имя {$this->_name} отсутствует в словаре";
                }
            } else {
                $this->_error = 'Неправильно указан пол!';
            }
        } else if ($case == 0) {
            $result = $this->_name;
        } else {
            $this->_error = 'Неправильно указан падеж!';
        }

        return (empty($this->_error)) ? (($this->_encode != 'UTF-8') ? utd8_decode($result) : $result) : $this->_error;
    }

}
Функция расчитана на склонение русских имён. Если вдруг я упустил какое-то имя, Вы можете самостоятельно добавить его. Для этого нужно во все 6 массивов добавить окончания этого имени.
Вот так вот она работает:
$smartName = new SmartName('Стас');
echo $smartName->dispose(4, 1); // Выведет имя в творительном падеже

После, я случайно наткнулся на сервис Яндекса Склонятор. А после на функцию, которая поможет склонять имена, фамилии и даже некоторые ники и слова. Хороша она тем, что не требует указания рода, и можно подавать на склонение целую фразу, например ф.и.о. Курьян Станислав Олегович. Со своей задачей она справляется превосходно. Вот код функции:

function inflect($name) {
    $url = 'http://export.yandex.ru/inflect.xml?name='.urlencode($name);
    $curl = curl_init($url);
    curl_setopt($curl, CURLOPT_USERAGENT, 'Opera/9.80 (Windows NT 6.1; U; ru) Presto/2.6.30 Version/10.61');
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    $result = curl_exec($curl);
    curl_close($curl);
    $cases = array();
    preg_match_all('#\<inflection\s+case\=\"([0-9]+)\"\>(.*?)\<\/inflection\>#si', $result, $m);
    if ( count($m[0]) ) {
        foreach ($m[1] as $i => &$id) {
            $cases[(int)$id] = $m[2][$i];
        } unset ($id);
    } else return null;
    if (count($cases) > 1) return $cases;
    else return false;
}

Как видите функция компактнее. Возвращает массив склонённых слов.
Для того, чтобы явно указать падеж, можно дописать ещё одну небольшую функцию s_name(), у которой в качестве входящих параметров выступает склоняемое имя и падеж.

function s_name($name, $p) {
    $cases = inflect($name);
    if ($cases && count($cases) > 1) return iconv("UTF-8", "Windows-1251", $cases[$p]);
    else return iconv("UTF-8", "Windows-1251", $name);
}

Функция возвращает склонённое имя в кодировке UTF-8, поэтому я использовал функцию iconv() для преобразования в CP1251.

Всё о веб-дизайне на новом проекте web-dizz.com, посвященном дизайну и графике.

Stas Kuryan

Web developer. Перфекционист в написании кода.

23 комментария

  1. Виктор     

    Здравствуйте, скажите пожалуйста как специалист.
    Ставлю под вашим кодом:
    [php]$tpl->set('{fullname_s}',$name. $s. $p." ".smart_name($row['fullname']));[/php]
    Почему то выводит 1 вИКТОР
    Я конечно полный ноль в php, но могли бы вы мне сказать как правильно это делается скажем в родительном падеже.
    И спасибо огромное что вы создали такою нужную вещь.

    • Stafox        Автор

      Все просто, для того, чтобы вывести имя в родительном падеже нужно использовать функцию со следующими входными параметрами:

      [php]

      smart_name($row['fullname'], $s, $p);
      [/php]

      А Ваш код, полагаю, будет выглядеть вот так:
      [php]
      $s = 1; //указывает что пол - мужской
      $p = 3; // задаём что падеж будет дательным
      $tpl->set('{fullname_s}', smart_name($row['fullname'], $s, $p));
      [/php]

      • Виктор     

        Огромное Вам спасибо, теперь я понял как это делается на примере.
        Мозайка сложилась! )
        Ещё раз спасибо!!

        • Stafox        Автор

          Пожалуйста. Рад был помочь.

  2. Дмитрий     

    Имя АРТУР не склоняется в родиnельном =((( КАК ДОБАВИТЬ?

    • Stafox        Автор

      В конец каждого массива m_end* добавьте окончания для склонения этого имени.
      т.е.
      [php]
      $m_end1 = array(..., 'ур'); //Именительный
      $m_end2 = array(..., 'ура'); //Родительный
      $m_end3 = array(..., 'уру'); //Дательный
      [/php]

  3. Руслан     

    во второй функции от яндекса у меня выбивает ошибку в 3 строке. Что это может быть?

  4. Руслан     

    оно пишет что неопределённая функция curl_init () … что это может быть, помогите мне очень надо

    • Stafox        Автор

      Это значит, что на сервере не установлен компонент curl
      Если сервер Ваш, то нужно установить пакет curl. Если нет, обратитесь к хостеру, возможно он сможет Вам помочь.

      • Руслан     

        я со всем разобрался, у меня теперь есть другой вопрос, как мне вытащит url на котором я нахожусь?

  5. Руслан     

    как мне сделать проверку если в url есть слово id и рядом с ним какая та цыфра то мы делаем одно иначе другое.

    • Stafox        Автор

      Если я правильно Вас понял, то так:

      [php]
      $id = (int) $_GET['id'];
      if ($id == 1)
      {
      echo 'Одно'; // Делаем одно
      }
      else
      {
      echo 'Другое'; // Делаем другое
      }
      [/php]
      Либо через switch-case:
      [php]
      $id = (int) $_GET['id'];
      switch ($id) {
      case 1: echo 'Раз'; // Делаем раз
      break;
      case 2: echo 'Два'; // Делаем два
      break;
      default: echo 'Остальное'; //Делаем остальное
      break;
      }
      [/php]

  6. Руслан     

    извините, я просто пишу быстрее чем думаю, я все сделал, теперь у меня люди в соц.сети выводят как в контакте id9 или id482

    • Stafox        Автор

      Так, а вот тут, я не совсем понимаю… Где выводятся? Какой движок? Фреймворк?

      • Руслан     

        сам пишу! не люблю движки.

        • Stafox        Автор

          Хорошо. Но если Вы не изложите сути проблемы, я не смогу помочь.

          • Руслан     

            все прошлые вопросы я решил, а то что у меня не получается много кода вам придется писать, если у вас есть скрипт обработки формы без перезагрузки страницы, тогда буду рад. Мой почему то накрылся, 2 формы работают норм а сейчас застрял, не передается методом POST переменные

            • Stafox        Автор

              Ну, можем сделать так: Вы показываете мне свои исходники, я пытаюсь помочь.

  7. Jin     

    А что на счёт кодировки?
    У меня сайт работает на UTF-8, а ваша функция работает только на Windows-1251….
    Иероглифы получаются в UTF8, а не имена…

    • Stafox        Автор

      Да, есть такая проблемка. Заменил на реализацию классом. Вышло короче, с кодировками проблем нет.

  8. Jin     

    Всё разобрался как можно сделать на Utf-8.

    $a= smart_name("Влад", "2","1");
    $ks = iconv("Windows-1251", "UTF-8",$a);
    echo $ks;
    

    Спасибо вам за хороший вариант.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *