Пользователь
0,0
рейтинг
22 октября 2013 в 14:07

Разработка → PHP RUtils — небольшая библиотека для обработки русского текста

PHP*
В работе на языке Python я частенько использую библиотеку Pytils для работы с русским текстом, и мне очень не хватало этой библиотеки при работе на PHP. Возможно, я искал плохо, но все, что я находил, не предоставляло подобных возможностей либо не слишком отвечало моим понятиям о чистом коде.

И вот, однажды я решил портировать библиотеку на PHP, и теперь хочу поделиться ею с народом и очень надеюсь на помощь в ее улучшении: буду рад советам, баг-репортам и особенно pull-реквестам. Библиотека находится на GitHub'е: github.com/Andre-487/php_rutils

UPD от 26.10.2013: теперь библиотека так же доступна через Composer: packagist.org/packages/andre_487/php_rutils
Так же хочу сказать спасибо всем, кто помог улучшить библиотеку и довести ее до стабильного релиза.

Возможности библиотеки


PHP RUtils — порт Pytils на PHP. Это утилиты для работы с русским текстом. Утилиты разделены на следующие модули (классы):

  • Numeral — работа с числами: склонение существительных в зависимости от количества, числа прописью, суммы денег в рублях и копейках прописью.
  • Dt — работа с датами: расширение формата дат PHP русскими именами месяцев, дней недели; временные периоды (например, 24 976 дней назад).
  • Translit — транслитерация, подготовка строк для использования в URL'ях, именах файлов.
  • Typo — небольшой набор правил типографики простого текста.


Примеры кода


Модуль Numeral

Выбор формы множественного числа

$variants = array(
    'гвоздь', //1
    'гвоздя', //2
    'гвоздей' //5
);
$amount = 15;
echo $amount, ' ', RUtils::numeral()->choosePlural($amount, $variants);
//Result: 15 гвоздей

echo RUtils::numeral()->getPlural(2, $variants);
//Result: 2 гвоздя

Выбор формы и вывод прописью

echo RUtils::numeral()->sumString(1234, RUtils::MALE, $variants);
//Result: одна тысяча двести тридцать четыре гвоздя

Вывод числа прописью

$numeral = RUtils::numeral();
echo $numeral->getInWordsInt(100);
//Result: сто

echo $numeral->getInWordsFloat(100.025);
//Result: сто целых двадцать пять тысячных

echo $numeral->getInWords(100.0);
//Result: сто

Вывод суммы денег в рублях

echo RUtils::numeral()->getRubles(100.25);
//Result: сто рублей двадцать пять копеек


Модуль Dt

Сегодняшняя дата

Параметры передаются в качестве инстанса класса \php_rutils\struct\TimeParams, так же возможно передавать их в виде массива

$params = new TimeParams();
$params->date = null; //это значение по умолчанию
$params->format = 'сегодня d F Y года';
$params->monthInflected = true;
echo RUtils::dt()->ruStrFTime($params);
//Result: сегодня 22 октября 2013 года

Историческая дата

Параметры передаются в качестве массива, поля такие же как в классе TimeParams.
Дата передается как строка в свободном формате. Так же возможно передавать дату как Unix timestamp или как инстанс класса DateTime.

$params = array(
    'date' => '09-05-1945',
    'format' => 'l d F Y была одержана победа над немецко-фашистскими захватчиками',
    'monthInflected' => true,
    'preposition' => true,
);
echo RUtils::dt()->ruStrFTime($params);
//Result: в среду 9 мая 1945 была одержана победа над немецко-фашистскими захватчиками

Временной период до фиксированной даты в прошлом

Форматы времени для данной функции аналогичны форматам для Dt::ruStrFTime.
Параметр $accuracy отвечает за подробность информации.

$toTime = new \DateTime('05-06-1945');
echo RUtils::dt()->distanceOfTimeInWords($toTime);
//Result: 24 976 дней назад

$toTime = strtotime('05-06-1945');
$fromTime = null; //now
$accuracy = 3; //дни, часы, минуты
echo RUtils::dt()->distanceOfTimeInWords($toTime, $fromTime, $accuracy);
//Result: 24 976 дней, 11 часов, 21 минуту назад

Временной период между фиксированными датами

$fromTime = '1988-01-01 11:40';
$toTime = '2088-01-01 12:35';
$accuracy = 3; //дни, часы, минуты
echo RUtils::dt()->distanceOfTimeInWords($toTime, $fromTime, $accuracy);
//Result: через 36 525 дней, 0 часов, 55 минут


Модуль Translit

//Транслитерация
echo RUtils::translit()->translify('Муха — это маленькая птичка');
//Result: Muha - eto malen'kaya ptichka

//Обратное преобразование
echo RUtils::translit()->detranslify("SCHuka");
//Result: Щука

//Подготовка для использования в URL'ях или путях
echo RUtils::translit()->slugify('Муха — это маленькая птичка');
//Result: muha---eto-malenkaya-ptichka


Модуль Typo

$text = <<<TEXT
...Когда В. И. Пупкин увидел в газете ( это была "Сермяжная правда" № 45) рубрику Weather Forecast (r),
он не поверил своим глазам - температуру обещали +-451F.
TEXT;

//Стандартные правила
echo RUtils::typo()->typography($text);
/**
 * Result:
 * ...Когда В. И. Пупкин увидел в газете (это была «Сермяжная правда» №45) рубрику Weather Forecast®,
 * он не поверил своим глазам — температуру обещали ±451°F.
 */


//Правила из набора "extended"
echo RUtils::typo()->typography($text, TypoRules::$EXTENDED_RULES);
/**
 * Result:
 * …Когда В. И. Пупкин увидел в газете (это была «Сермяжная правда» №45) рубрику Weather Forecast®,
 * он не поверил своим глазам — температуру обещали ±451 °F.
 */

//Пользовательские правила
echo RUtils::typo()->typography($text, array(TypoRules::DASHES, TypoRules::CLEAN_SPACES));
/**
 * Result:
 * ...Когда В. И. Пупкин увидел в газете (это была "Сермяжная правда" № 45) рубрику Weather Forecast (r),
 * он не поверил своим глазам — температуру обещали +-451F.
 */

Так же (это не получится показать здесь) модуль Typo расставляет неразрывные пробелы (NBSP, THIN NBSP). Этот модуль рассчитан больше на простой текст, поэтому специальные символы HTML не используются, используются непосредственно символы UTF-8.

Подробности


Когда я портировал Pytils, я старался реализовать всю функциональность и сделать интерфейсы узнаваемыми, чтобы другие пользователи Pytils могли без труда воспользоваться PHP-версией. Так же большая часть тест-кейсов взята из тестов Pytils. Так же я позаимствовал из репозитория Pytils многие комментарии и примеры. Но, конечно же, не обошлось без «улучшений» в связи с моим авторским видением и спецификой PHP.

Например, было решено изменить сигнатуру метода Dt::ruStrFTime и вместо большого списка аргументов использовать структуру. В Пайтоне не случится драмы, если вдруг из огромного числа аргументов, имеющих значения по умолчанию, нужно изменить только последний — именованные аргументы придут на помощь; но если подобная ситуация случится в PHP, это будет настоящей трагедией. А параметров у функции прилично.

Так же, так как часто разработчики на PHP не любят передавать в функцию объект класса, но любят передавать массив, реализована возможность передавать все эти параметры в виде ассоциативного массива.

Так же учтены предпочтения разработчиков на PHP относительно дат, я постарался сделать работу с ними максимально комфортной. Как известно, со временем в PHP частенько работают в двух форматах: свободный строковой (02.03.2005, 2005-03-02) и Unix timestamp. Так же порой используется класс DateTime. Я решил, что функции, работающие с датами, должны работать со всеми тремя этими типами. Так же я добавил возможность указать временную зону для передаваемого времени.

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

Все примеры, приведенные в статье доступны в виде скриптов, приспособленных к выполнению как на веб-сервере, так и в консоли (в том числе в консоли Windows с ее cp866).

В данный момент я выбрал версию 0.1 для своей библиотеки, но я надеюсь, что для нее найдутся пользователи, которые помогут развивать ее дальше, и она доберется до версии 1.0 и даже, чем черт не шутит, 2.0.
Андрей Прокопюк @Andre_487
карма
69,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (26)

  • +5
    Пожалуйста, опубликуйте вашу работу на Packagist.
    • +3
      Я планирую сделать это, если в ближайшее время не найдется неучтенных багов. А если найдутся — после их исправления.
  • +4
    Круто. Форкнул, перепилю на мову :)
    • 0
      Дайте угадаю название: UUtils? :)
      • +2
        UAtils :)
        • +4
          Упс, я что-то тупанул и подумал что тут главное Utils от утилит. Ну тогда уже можно UAtits, но это уже другая история :)
          • 0
            tits ;)
            • 0
              RUtits, ENtits, UAtits… Какой сложный выбор, я пожалуй выберу UAtits!
  • +1
    Класс! Вчера только увидел порт библиотеки Requests github.com/rmccue/Requests а тут и Pytils подоспел — отличная работа, спасибо.

    Еще бы теги для Twig и Smarty для полного сходства)
    • 0
      Спасибо )

      Может быть, они будут. Может быть, их даже добавит кто-нибудь из сообщества )
  • +1
    Супер!
    Еще бы от дублирующих дефисов избавиться
    //Подготовка для использования в URL'ях или путях
    echo RUtils::translit()->slugify('Муха — это маленькая птичка');
    //Result: muha---eto-malenkaya-ptichka

    //Result: muha-eto-malenkaya-ptichka
    • +1
      Я думал об этом, когда портировал эту часть. Но решил оставить так, чтобы показывать, что помимо тире в оригинале были пробелы.
  • +3
    Да, с packagist-ом не тяните! Сразу выкладывайте! Баги по ходу использования поправятся…
    • 0
      Вероятно, в эти выходные разберусь с первыми issues и выложу.
  • +1
    echo RUtils::dt()->distanceOfTimeInWords($toTime, $fromTime, $accuracy);
    //Result: через 36 525 дней, 0 часов, 55 минут
    


    0 часов — косяк, от которого нужно избавиться.
    • 0
      Я перенял это из оригинала, но действительно, от этого надо избавиться.
    • 0
      Проблема уже исправлена первым pull-реквестом.
  • 0
    Для PHP 5.4 проще воспользоваться intl. Ко всем функциям получим из коробки поддержку не только русского.
    • 0
      Все мы знаем, как много intl привнес в Yii2 ) Но, если не ошибаюсь, умеет он не совсем все из этого и все-таки требует написания некоторых оберток для удобства использования. Или я ошибаюсь?
      • 0
        Множественные формы, вывод числа прописью, форматирование сумм денег, даты intl умеет в 5.3. Транслит в 5.4.

        Типографику не умеет.

        Обёртка в Yii2 сделана для использования именованных параметров в сообщениях. Если использовать позиционные или пользоваться форматтерами напрямую, а не в переводах, обёртка не нужна.
      • +2
        В любом случае intl предъявляет определённые требования к серверу и на shared-хостинге его могут выпилить или не включить. Так что для продуктов штука определённо полезная.
  • 0
    Есть возможность в getPlural получить только вариант слова, без цифры («яблок», вместо «5 яблок»)?
    Случай использования:
    дополнительное форматирование у числа (пример, <strong>5</strong> яблок).

    Оставил заявку github.com/Andre-487/php_rutils/issues/15
    • 0
      Да, есть. Функция Numeral::choosePlural просто выбирает форму слова.
  • +2
    Теперь библиотека доступна через Composer: packagist.org/packages/andre_487/php_rutils
  • +1
    //Alphabet (ISO9 [ГОСТ 7.79—2000], Scheme B)
    


    Стандарт для транслита вы кажется выбрали далеко не самый лучший. Русская Х превращается в английскую X («особых» -> «osobyx»), Ц в CZ («цепочка» -> «czepochkа»).
    • 0
      Мне тоже это не особенно нравится, но это актуальный стандарт.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.