Пишу, по большей части, про историю, свою жизнь и немного про программирование.

PHP и UTF-8: второй этап (продолжение: ничего не забыли?)

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

Выяснить какие же расширения реально используются в проекте нам поможет следующая программа:

<?
    $extensions = array_merge(
        get_loaded_extensions(true),
        get_loaded_extensions()
    );

    $funcs = array();

    foreach ($extensions as $extension) {
        if ($extension == 'standard' || $extension == 'session') {
            continue;
        }

        $extfuncs = get_extension_funcs($extension);

        if ($extfuncs) {
            $funcs = array_merge(
                $funcs,
                    array_combine(
                        array_map('strtolower', $extfuncs),
                        array_fill(0, sizeof($extfuncs), $extension)
                )
            );
        };
    }

    $classes = get_declared_classes();

    set_time_limit(0);

    $classes_regexp = '/new\s+(?i)(' . implode('|', $classes) . ')/s';
    $funcs_regexp   = '/(' . implode('|', array_keys($funcs)) . ')/si';

    $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('.'));
    
    for (;$it->valid(); $it->next()) {
        $ext = pathinfo($it->getBaseName(), PATHINFO_EXTENSION);

        if ($ext == 'php' || $ext == 'tpl' || $ext == 'inc') {

            $content = file_get_contents($it->key());

            if (preg_match_all($classes_regexp, $content, $matches)) {
                echo $it->key(), "\nClass: ", 
                    implode("\nClass: ", array_map('strtolower', $matches[1])), "\n\n";
            }

            if (preg_match_all($funcs_regexp, $content, $matches)) {
                echo $it->key(), "\n";

                foreach ($matches[1] as $func) {
                    $func = strtolower($func);

                    if (isset($funcs[$func])) {
                        echo 'Module: ', $funcs[$func], "\n";
                    } else {
                        echo 'Something wrong: ', $func, "\n";
                    }
                }

                echo "\n\n";
            }            
        }
    }

У меня она отработала достаточно неспешно, за несколько минут. Каковы же результаты?

bolk@dev:~/daproject$ awk -F: '/^Module/{print $2}' tool2.log | sort -u
 ctype
 curl
 date
 gettext
 hash
 iconv
 …

bolk@dev:~/daproject$ awk -F: '/^Class/{print $2}' tool2.log | sort -u
 exception
 simplexmlelement

Начнём с конца — Exception это класс для работы с исключениями, с UTF-8 он вполне себе дружит, SimpleXMLElement (как и расширение SimpleXML, откуда этот класс) так же работает с UTF-8.

А вот остальные 19 модулей, используемых в проекте, просмотреть сложнее. Их тех, что у меня есть я сразу могу исключить SQLITE3, XML, XML-RPC, Zlib, SimpleXML, PCRE, JSON, MySQL, hash, intercept и date — они с UTF-8 дружат, сразу могу сказать, что ctype с ним не работает, а к использованию модулей iconv и mbstring надо присмотреться — значит где-то используются функции конвертирования. Эти ситуации придётся разбирать вручную.

Как я узнаю какие модули работают с UTF-8, а какие нет? Их все можно разделить на четыре группы: придуманы, чтобы работать с разными кодировками (iconv, mbstring, PCRE), нейтральны к любой кодировке (date, intercept, hash), «я точно знаю, что с UTF-8 не дружат» (ctype), «не знаю, надо попробовать» (syck, sanitize).

На что ещё надо обратить внимание? На две вещи — нет ли в файлах где-то явного указания кодировки и не используются ли модули PEAR.

С первым просто, я перевожу проект с windows-1251, значит искать будем эту кодировку:

bolk@dev:~/wiki$ find -type f -name '?*.*' | grep -v '.svn' | xargs -r -n1 egrep -iH 'cp1251|cp\-1251|windows\-1251'
./php/dir1/file1.php:         if (strtolower($rss->encoding) != 'windows-1251') {
./php/dir1/file2.php:                 $c = @iconv($rss->encoding, 'cp1251', $c);
./php/dir2/file3.php:                    $title = iconv('UTF-8', 'CP1251', $employers[$i]->first_name .' '. $employers[$i]->last_name);
…

У меня таких строк — вагон и маленькая тележка, что с ними делать, будем решать дальше, пока же отметим, что они есть, причём в количестве 158 штук.

А что с PEAR? Точнее, с теми файлами PEAR, которые подключаются не из папки проекта, а из папки PEAR, которая скрыта где-то в операционной системе? Тут всё до предела просто — откройте проект в своей любимой среде программирования (IDE), какой-нибудь Zend Studio или Eclipse, она покажет вам все импорты, которые есть в вашем проекте. Если видите что-то внешнее, то не забудьте включить и эти файлы в исследование.

У меня таких импортов, кстати, не оказалось.

Кажется, информации накоплено достаточно, пора что-то изменить в проекте.

(И тут я предлагаю сделать перерыв до завтра)

1 комментарий
Жеже? 2010

Болк — торт!