PHP и UTF-8: третий этап (давайте уже что-нибудь заменим)

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

Следующая программа поможет нам заменить наши ущербные функции (их имена и статистика всё так же лежат в файле tool-func.log) на пока не менее ущербные обёртки.
<?
    if ($_SERVER['argc'] <> 3) die;
    if (realpath($_SERVER['argv'][2]) == realpath(__FILE__)) die; 

    $file = file_get_contents($_SERVER['argv'][2]);
    if (strpos($file, '<?') === false) die;

    $funcs = file($_SERVER['argv'][1], FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    foreach ($funcs as &$func) {
        list(,$func) = preg_split('/\s+/s', $func, 2, PREG_SPLIT_NO_EMPTY);
    }

    $skip   = false;
    $skipIf = array(T_PAAMAYIM_NEKUDOTAYIM, T_DOUBLE_COLON, T_OBJECT_OPERATOR);
    $fixed  = false;

    ob_start();

    foreach (token_get_all($file) as $token) {
        if (is_scalar($token)) {
            echo $token;
        } else {
            if (!$skip && $token[0] == T_STRING && in_array(strtolower($token[1]), $funcs)) {
                $fixed = print 'Utf::' . $token[1];
            } else {
                echo $token[1];
            }

            $skip = in_array($token[0], $skipIf); 
        }
    }

    file_put_contents($_SERVER['argv'][2], ob_get_clean());

    if ($fixed) {
        echo $_SERVER['argv'][2], "\n";
    }
Запустить её можно, например, так (напоминаю, что у меня PHP находится в файлах с расширениями php, tpl и inc):
bolk@dev:~/daproject$ find -type f \( -name '*.php' -or -name '*.tpl' -or -name '*.inc' \) | grep -vF '.svn' |
> xargs -n1 php tool4.php tool-func.log
./php/file.php
./php/dir1/file1.php
./php/dir1/file2.php
./php/dir1/dir2/file3.php
…
Программа напечатает имена файлов, где произошла замена. Своеобразная проверка — запустите её ещё раз, на втором проходе никаких имён файлов выведено быть не должно! Обязательно проследите, чтобы запуск не затронул ваш класс Utf!

Итак, мы заменили функции на вызовы класса Utf. Всё работает? У меня не заработало. Причины две — я ошибся в имени одной из функций в классе Utf и появились проблемы с preg_replace_callback. О последней проблеме поподробнее.

Пока preg_replace_callback была встроенной функцией всё было замечательно, как только она стала статическим методом класса, проблемы появились. Всё дело в указании в качестве callback метода статического класса. Во-первых, метод может оказаться private или protected, что мешает ему быть вызванным из другого, неродного класса, во-вторых, в качестве имени класса может быть указано «self», а «self» внутри класса Utf равно «Utf», что совсем не то, что ожидается.

Такие вещи придётся править вручную. Если нет возможности провести тестирование, то можно воспользоваться подсказками IDE — во многих случаях она укажет на такие ситуации, но, в любом случае, лучше все такие места просмотреть глазами.

Сегодня ночью я улетаю в Екатеринбург, так что, думаю, это последняя часть на этой неделе.

Кстати, остаются ещё строковые функции, вызываемые как callback, внутри eval или в eval-выражениях preg_replace. Что-то с ними надо делать.
9 сентября 2010 18:26

diiina (инкогнито)
9 сентября 2010, 19:50

О! Кат! Ура, я считаю :)

livejournal.com (инкогнито)
10 сентября 2010, 00:38

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

bolk (bolknote.ru)
10 сентября 2010, 01:15, ответ предназначен livejournal.com

Это, увы, ещё не конец :) Прилечу из Екатеринбурга, продолжу.

indeec17 (инкогнито)
10 сентября 2010, 20:06, ответ предназначен bolk (bolknote.ru):

Я нашёл универсальное решение проблемы со строковыми функциями с UTF-8, -16 и даже с китайским языком, это в т.ч. решает проблемы с eval и все вышенаписанные. Достаточно тебя внедрить в команду разработчиков php! Дальше произойдёт всё само собой =)

bolk (bolknote.ru)
10 сентября 2010, 22:12, ответ предназначен indeec17

Это очень дорогое решение :) Мой бывший начальник иногда меня спрашивает «где нам найти ещё трёх Болков» :)

Ваше имя или адрес блога (можно OpenID):

Текст вашего комментария, не HTML:

Кому бы вы хотели ответить (или кликните на его аватару)