Травма плеча перестала беспокоить

«Загадочная травма», о которой я писал меньше месяца назад отпустила. Как только боль прошла, в тот же день смог поднять «разогревочные» 68 кг, около 10 раз. Это, конечно, несколько меньше, чем обычно (обычно — до 18-ти), но отлично по сравнению с тем, что пока у меня что-то там болело, я не мог поднять 68 ни разу.

Теперь пока боюсь сорвать, делаю только один подход в день и смотрю на реакцию, надо увеличивать количество подходов, потом — вес.

Оставить комментарий

Пакетное скругление картинок: оболочка

Оболочка (8.03КиБ)

Пока у меня нет свободного вечера, чтобы заняться «скруглятором», но это не значит, что с проектом ничего не происходит. Читатель с ником oldTV изготовил отличную графическую оболочку «MCShell». Выглядит замечательно.

12 комментариев

PHP и UTF-8: третий этап (создание прототипов)

Этот сериал хорош тем, что я пишу его в реальном времени. Единственно, первая фаза — когда я собирал первоначальные данные и обдумывал как мне подступиться к этой задаче, заняла куда больше времени, чем может показаться. Итак, в предыдущей части мы выяснили какие модули используются в нашем проекте и узнали, что в нём кое-где указана кодировка прямым текстом. Так же держим в голове, что нам надо что-то будет делать с файловыми функциями и обращением к символу в строке по индексу ($str[$index]).

В этой части мы подготовимся к тому, чтобы заменить в проекте все упоминания небезопасных (с точки зрения работы с UTF-8) функций на статические методы класса Utf, где мы будем менять их на UTF-8-аналоги.

Что же дальше?
Вообще-то, я мог бы, конечно, вручную описать все 38 функций моего проекта, но мы же умеем программировать! Почему бы этим не воспользоваться? PHP даёт широкие (но, к сожалению, глючные) средства для исследования функций — это специальные классы расширения Reflection, которыми мы и воспользуемся.

<?
function U_makeproto($funcname)
{
    $func = new ReflectionFunction($funcname);

    $out = '    static public function ' . $funcname . '(';

    $counter = 0;
    $hasRefOptional = $hasOptional = false;
    $names = array();

    foreach ($func->getParameters() as $param) {
        $name = $param->getName();

        if ($name !== null && $name != '...') {
            $names[] = $name = '$' . $name;
        } else {
            $names[] = $name = '$param' . $counter++;
        }

        if ($param->isPassedByReference()) {
            $name = '&' . $name;
        }

        if ($param->isArray()) {
            $name = 'array ' . $name;
        }

        if ($param->isOptional()) {
            $name .= '=';
            $name .= $param->isDefaultValueAvailable() ? $param->getDefaultValue() : 'null';

            if ($param->isPassedByReference()) {
                $hasRefOptional = true;
            }

            $hasOptional = true;
        }

        $out .= $name . ', ';
    }

    $out = rtrim($out, ', ') . ') {';
    if ($counter || !$names) {
        $out .= "\n        // FIXME: Invalid proto ( https://www.php.net//$funcname )\n";
    }

    $out .= "\n        if (self::ON) {".
            "\n            // TODO: UTF version".
            "\n        }\n";

    if (!$hasRefOptional) {
        if ($hasOptional || !$counter && !$names) {
            $out .= "\n        " . '$params = func_get_args();'.
                    "\n        return call_user_func_array('$funcname', \$params);";
        } else {
            $hereNames = implode(', ', $names);

            $out .= "\n        return $funcname($hereNames);";
        }
    } else {
        $reqCounter = $func->getNumberOfRequiredParameters();
        $out .= "\n        switch (func_num_args()) {".
                "\n            case $reqCounter:".
                "\n                " . '$params = func_get_args();'.
                "\n                return call_user_func_array('$funcname', \$params);";

                for ($i = $reqCounter+1, $len = $func->getNumberOfParameters(); $i<=$len; $i++) {
                    $hereNames = implode(', ', array_slice($names, 0, $i));

                    $out .= "\n            case $i:".
                            "\n                return $funcname($hereNames);";
                }

                $out .= "\n            default:".
                        "\n                 trigger_error('Wrong parameter count for $funcname()', E_USER_WARNING);".
                        "\n                 return null;";

                $out .= "\n        }";
    }

    return $out . "\n    }";
}


echo "<?php\nclass UTF\n{\n    const ON = false;\n\n";
foreach (file('php://stdin', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $funcname) {
    echo U_makeproto($funcname), "\n\n";
}

echo "}";

Как я уже сказал, расширение Reflection работает в PHP с багами, именно поэтому я максимально подробно стараюсь обработать ошибочные ситуации. После запуска программы, можно подсчитать, какое количество функций (у меня их 10) не отобразилось правильно, ничего не поделаешь, придётся их править руками. Файл tool-func.log содержит частоты появления функций с их именами из первого этапа эпопеи.

bolk@dev:~/daproject$ sed -re 's/\s+\S+\s+//' tool-func.log | php tool-func-proto.php > utf.php
bolk@dev:~/daproject$ grep -c FIXME utf.php
10

Я удаляюсь править прототипы руками, а в следующей части уже перейдём к непосредственной замене.

P.S. В коде есть странное место — «blah» . ’\$var’, увы, у меня какие-то глюки с форматированием на сайте, пришлось сделать так.

Оставить комментарий

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. Что-то с ними надо делать.

5 комментариев

Ограничение сна: FAIL

Всё, хватит, заканчиваю свой эксперимент по ограничению количества сна. Мне, чтобы выспаться, нужно около 9…10 часов, я ограничивал время сна до 5…8 часов в день. Появились все признаки усталости: сонливость, рассеянность. Правда, сегодня всё будет ещё хуже — у нас вылет в Екатеринбург в половину первого, а в 8:15 уже подъём.

Не знаю, буду ли читать комментарии. Как получится.

Оставить комментарий