программирование
PHP, UTF-8: восьмой этап, заключительный
Прошло уже немало времени, с тех пор как я описал предыдущий этап перевода
Для начала надо понять что же делать с чтением и записью файлов. Нет никакого автоматического способа решить что делать с этими операциями в вашем проекте, хорошие новости заключаются в том, что, скорее всего, ничего делать не придётся. Мне не пришлось, хотя, чтобы это понять, нужно было исследовать исходники. Побайтовое чтение файлов — редкая операция в мире Даже в том случае, если вы производите с файлом Для другой операции, нам понадобится четыре метода в классе работы с UTF (напоминаю, что константа ON этого класса определяет включена ли поддержка UTF): метод charset возвращает Замены очевидны: везде, где в коде встречается Потом нужно проверить все места, где встречаются вызовы chr и ord («egrep ’\\b(ord|chr)’ -R .») и проверить не нужно ли переписать После этого осталось только перекодировать исходный код: И базу: В процессе пуска проекта выяснилась ещё одна немаловажная подробность (спасибо Саше Покатилову): оказывается, несмотря на то, что установки локали (setlocale) никак не влияет на функции сортировки в PHP, она влияет на некоторые функции работы с файлами (dirname, basename и некоторые другие), вот цитата из описания функции dirname: dirname() is locale aware, so for it to see the correct directory name with multibyte character paths, the matching locale must be set using the setlocale() function. Поэтому не забывайте выставлять локаль, если пользуетесь этими функциями. Дополнено: внезапно нашлись неприятные грабли, будьте осторожны: функция preg_match_all (и preg_match) с ключём PREG_OFFSET_CAPTURE возвращает бинарное смещение в строке (т. е. указывающее не на байт, а на символ), даже Я поступил просто — написал простую функцию, которая корректирует выходные значения. Идея простая: отрезаем при помощи substr строку до смещения, потом измеряем её Еще позднее: оказывается и параметр offset (последний параметр в preg_match и preg_match_all) работает с бинарным смещением, И ещё позже: а вот вам ещё сюрприз: модуль Multibyte String считает, что кодировка И ещё сильно позже: strftime тоже использует локаль.Что же дальше?
# вот эти файлы перекодировать не надо, они у меня уже в UTF-8
SKIPIT='./php/Utf.php|./php/UtfReverseString.php|./php/UtfString.php'
tmp=`tempfile`
for line in $(find -name '*.php' -or -name '*.tpl' -or -name '*.inc' | egrep -v "$SKIPIT"); do
iconv -fcp1251 -tutf8 "$line" -o "$tmp"
[ -e "$tmp" ] && mv -f "$tmp" "$line"
donemysqldump -pпароль -uпользователь --add-drop-table --create-options --default-character-set=utf8 --order-by-primary -q база |
sed -e 's/cp1251;$/utf8;/mg' |
mysql -pпароль -uпользователь база
Даже создатели Dojo не читают документацию
Class Sample
Public Property Let someprop(val)
…
End Property
Public Property Set someprop(val)
…
End Property
Public Property Get someprop
…
End Property
End ClassДва вида сеттеров связаны с тем, что в VBScript довольно чётко различаются передача по ссылке и по значению (например, можно в параметрах функции указать «ByRef» или «ByVal»), кроме того, у сеттеров и геттеров есть ещё один параметр, благодаря чему можно перехватывать вызов методов и делать совсем уж странные, с точки зрения JavaScript, штуки.
Впрочем, к чёрту подробности.
Один из разработчиков
Но в IE уже есть специальный метод объекта window, который занимается тем же — execScript, вторым параметром надо задать «vbscript» и voilà!, мы вызываем VBScript из JavaScript.
Я этот метод
Как заменить одну строку на другую в файле (Linux)
Устал я от долгих споров, давайте
Вот несколько способов, которые я знаю.
replace 'hello' 'bye, bye!' -- myfileСамое замечательное, что она умеет менять пары подстрок, что позволяет производить такие замены:
bolk-dev ~ $ echo ABBA | replace A B B A --
BAABВторой способ — создать временный файл, заменить в нём символы или подстроки
T=`/bin/tempfile`
sed 's/hello/bye, bye!/g' myfile > $T
mv $T myfileСразу в тот же файл записывать результат нельзя, операция „>“ сразу же откроет файл на запись и обнулит его. Поможет утилита sponge (входит в пакет moreutils):
sed 's/hello/bye, bye!/g' myfile | sponge myfileОна запишет stdin в файл разом, после того как все данные будут получены.
Добавлено: Азат в комментариях подсказывает, что можно воспользоваться ключом
sed -i 's/hello/bye, bye!/g' myfile
Кроссбраузерный data URI в CSS-файлах (подробно)
Кстати, всё забываю порекомендовать статью Banderlog’а, который написал её по мотивам моей статьи «Кроссбраузерный data URI
Статья на Хабре называется «К вопросу о кроссбраузерных Data URI».
Что за проект я перевожу на UTF: FAQ

Из комментариев на одном из сайтов Димы видно, что люди делают
Так что небольшое FAQ.
В: Долго ещё до конца сериала? О: Следующая часть будет последней.
B: Что за проект переводится
B: Зачем переводится? О: Все остальные внутренние сервисы «Яндекса»
B: Нельзя ли переписать это всё на другой язык, где нет проблем
В: Зачем нужно было всё автоматизировать? О:
В: Почему бы нанять сотню студентов, за копейки, чтобы они сделали замены руками? О: За этими студентами нужно проверять работу, кто это будет делать? Я за собой вычитывал исходники, распечатывал листы diff’ов, уходил
PHP, UTF-8: седьмой этап, давайте что-нибудь сделаем с регулярными выражениями
В прошлый раз я рассматривал, по сути, самый сложный этап — замену всех вхождений $var[$index] и им подобных на вызов моей функции. В процессе замены я несколько раз корректировал заметку и доводил некоторые вещи руками.
В самой первой части я рассказывал о том как можно решить проблему К сожалению, этот «глагол» появился только в PCRE 8.10, а в самом последнем PHP, на сегодняшний момент, содержится 8.02. Как я говорил ранее, новый PCRE можно вкомпилить в PHP при помощи специального ключа. Внутренние сервисы, где я работаю, используют Поскольку мы сейчас переходим постепенно на новую версию Ubuntu (с Hardy Heron на Lucid Lynx), то часть машин у нас пока на Цапле, а часть — уже на Рыси. Это значит, что пакетов нужно будет собрать шесть. Дело это непростое. Я решил попробовать «выдрать» из внутренностей PHP модуль PCRE, переименовать его функции и константы и поставить его как простой подключаемый модуль. Мне удалось сделать, модуль «свежего PCRE» (можно скачать в разделе «Храню»). Модуль называется ygx, все функции начинаются с префикса «ygx_», константы — с «YGX_». Что это за сокращение, не спрашивайте, когда я начинал работать, я это знал, а сейчас забыл. Сложнее всего было корректно переименовать только те функции, типы, константы и так далее, которые видно в PHP, не задев всё то, что импортируется из внешней библиотеки PCRE. Ставится модуль примерно следующим образом: потом нужно прописать в php.ini строку «extension=ygx.so», перезапустить Поскольку к этому моменту все вызовы функций PCRE у меня заменены на вызовы одноимённых методов статического класса UTF, то мне нужно поменять «preg_» на «ygx_» только в одном месте — внутри методов этого класса.Что же дальше?
mkdir /tmp/pcre
cd !:1
wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.10.tar.bz2
tar xvfj pcre-8.10.tar.bz2
cd pcre-8.10
./configure --prefix=/usr/local/pcre-8.10 --enable-utf8 --enable-unicode-properties --disable-static
make && make install
cd -
wget /files/php_fresh_pcre.zip
unzip php_fresh_pcre.zip
phpize
./configure --with-ygx-regex=/usr/local/pcre-8.10
make && sudo make install
phpize --clean
cd ; rm -rf /tmp/pcre
ICQ опять пытается стряхнуть альтернативные клиенты
На данный момент не подключаются QIP, Jimm и Miranda (возможно ещё
Добавлено позднее: удалось соединиться через Миранду 0.9.9b1, выключив «Secure connection (SSL)».
Flashblock в «Опере»
Оказывается, в «Опере» 10.50 и выше есть встроенный аналог плагина «Flashblock» для Firefox — опция «opera:config#UserPrefs|EnableOnDemandPlugin» (вставьте в адресную строку и нажмите Enter, увидите соответствующую настройку).
Опция включает функционал, который мне хотелось включить
IE9 и CSS3 2D Transforms
Появился Internet Explorer 9 PP6, из интересного — появилась поддержка
Кроме того, появилась поддержка семантических тегов HTML5 (section, nav, article и так далее). Раньше при разборе дерева элементы, содержащие эти теги наследовались от HTMLUnknownElement, теперь — от HTMLElement.
Stoyan Stefanov

Стоян Стефанов (архитектор YSlow, автор нескольких книг по JavaScript и ряда любопытных исследований) сейчас в «Яндексе» рассказывает лекцию. К сожалению, я пойти не смог, а жаль:
— Болк, а ты чего не на Стояне? Он тут тебя упомянул =)
— Э… как это?
— Он щас в Яндексе читает лекцию, ну и типа сказал вот крутой чувак (и твоего блога скриншот)
Hello, Stoyan! Sorry I could not attend your lecture.
PHP, UTF-8: шестой этап, она же «строки, часть II». Заменяем
В прошлой части я пытался упростить себе задачу, посчитав сколько различных видов операций получения символа из строки по индексу есть у нас в коде. Но для того, чтобы подсчитать эти ситуации, нужен парсер, который эти случаи выделит.
Я такой парсер начал писать несколько дней назад и очень быстро увидел, куда меня этот путь ведёт. К сожалению,
Код я выкинул и начал писать заново, основываясь на токенах (лексемах), которое выдаёт встроенное в язык API токинайзера. По ходу процесса я часто уточнял случаи и производил тестирование. Получился довольно неряшливый парсер, к тому же с некоторыми багами, которые я счёл несущественными. Например, не обрабатывается ситуация isset($var, $var[$idx]).
В PHP isset (а так же empty и unset) — конструкции, а не функции и принимают на вход только переменные. У меня получение символа по индексу заменяется на Utf::get(…), а внутри строки на другую конструкцию — $_ENV[0x5f3759df]->get(…), так что если произойдёт замена внутри isset, будет синтаксическая ошибка.
Т. е. у меня в коде, внутри файла с классом Utf есть ещё и такая строка — «$_ENV[0x5f3759df] = new Utf();». $_ENV я выбрал за то, что это переменная (а переменную внутри строки проще заменять на другую переменную, пусть и с вызовом метода, PHP это позволяется) и это, в терминах PHP, «суперглобальный» массив, то есть он доступен отовсюду.
Итак, я сказал, что парсер имеет баги, но на весь довольно большой код у меня было всего четыре случая, правда один довольно странный — пропала треть исходного кода, так что, если будете применять — сохраните оригинальный код (мой лежал в SVN).
Первая операция, которую вам нужно сделать ещё до запуска парсера — это запустить следующий скрипт. У меня парсер работает
Так что я перекодировал код туда и обратно и сравнил что получилось:
for file in $(find -name '*.php' -or -name '*.inc' -or -name '*.tpl'); do
sum_new=`iconv -fcp1251 -tutf8 "$file" | iconv -futf8 -tcp1251 | md5sum -b | cut -d' ' -f1`
sum_orig=`md5sum -b "$file" | cut -d' ' -f1`
if [ $sum_new != $sum_orig ]; then
echo $sum_new $sum_orig $file
fi
doneУ меня ничего не поломалось, так что я спокойно запустил пасер. После запуска я проверил синтаксис скриптов интерпретатором и вручную избавил четыре найденные ошибки:
find -name '*.php' -or -name '*.inc' -or -name '*.tpl' | xargs php -lПосле всех запусков проект выглядит рабочим, но в понедельник я обязательно буду тестировать весь проект.
Добавлено позднее: а вот как выглядит метод get в классе Utf:
static public function get($str, $index)
{
if (!is_scalar($str)) {
return isset($str[$index]) ? $str[$index] : null;
}
return self::substr($str, $index, 1);
}Добавлено позже: нашёл ещё два бага: неверно отрабатываются
Конструкции heredoc я нашёл при помощи «grep -FR ’<<<’ *» и просмотрел их все, их было очень мало и ни одна из них не содержала переменных.
Добавлено ещё позже: опаснее оказалась ситуация, которую просто так можно и не заметить: замена вызова func($var[$index]) на func(Utf::get($var, $index), где прототип func описан как func(&$var). Синтаксис PHP вполне допускает такие ситуации, а на деле в массив могут не попасть изменения, которые должны были переданы быть по ссылке.
Способов исправить такую ситуацию два. Первый — логгировать все вызовы Utf::get, которые применяются к массивам и править их на получение переменной по индексу. Второй — найти и просмотреть все такие ситуации. Я выбрал второй способ:
find \( -name '*.php' -or -name '*.tpl' -or -name '*.inc' \) |
xargs egrep -oh 'ion[\t ]+\w+[\t ]*([^{]+' | awk -F'[( ]' '/&\$/{print tolower($2)}' |
sort -u | egrep -v '^preg_' | xargs -I{} egrep -iR --exclude-dir=.svn {}'[\t ]*(.*?Utf::get' *Этот скрипт (который очень долго работает) показывает все подозрительные строки (у меня их 7 оказалось), их надо просмотреть, выявить потенциально проблемные и поправить, если проблема действительно есть.
И ещё позже: так же есть набор встроенных функций, которые используют передачу массива по ссылке, я их нашёл вот таким способом:
(cat<<FUNCS
'\b(?:array_multisort|array_pop|array_push|\
array_replace_recursive|array_replace|\
array_shift|array_splice|\
array_unshift|array_walk_recursive|\
array_walk|arsort|asort|current|each|\
end|key|krsort|ksort|natcasesort|natsort|\
next|pos|prev|reset|rsort|shuffle|\
sort|uasort|uksort|usort)[ \t]*\([\t ]*[^$)]'
FUNCS
) | xargs -I[] find \( -name '*.tpl' -or -name '*.php' -or -name '*.inc' \) -exec \
egrep [] {} \; -printВышла альфа «Оперы» 11 с расширениями
Это очень хорошая новость — вышла
Оно позволяет одним нажатием кнопки открыть в телефоне (на «Андроиде») ту же страницу, что сейчас открыта в браузере на компьютере, чтобы, например, продолжить её чтение уже с мобильника.
Из других хороших новостей — появилась поддержка WebSockets.
Добавлено: а ещё можно добавить сервисы «Яндекса» — пробки, погоду и отзывы о текущей странице.
Карго-культ личности
Это настолько смешно (для тех, кто понимает), что хочется процитировать полностью. Но только избранное:
Также пока меня не было (последние 2 недели я особо не писал в блог) удалось слить движок Nudnik.ru.
Движок очень хороший. Не побоюсь этого слова Мастер Смирнов постарался на славу (демо естественно у него на его сайте). Но также очень много файлов которые вообще не нужны.
Также есть аспекты в php когда функцию можно обработать в одну строчку, а он
зачем-то бабахаетв 5-ть. В результате все это добро весит 25 мб. Но это добро не грузит хостинг — это радует. А также он требует PHP 5 версии, а значит Смирнов «ухаживает» за своим детищем.А также товарищ Болк постарался с движком. И естественно «намудрил» с ним, где разберется только он сам.
Кстати, руку могу дать на отсечение, что движок Димы весит меньше сотни кг, написан без всякого форматирования и не использует буквально ничего, что появилось в PHP5.
Кстати, Дим, а кто этот Кирилл Котиков,
404fest: было здорово!
Всё забываю написать, на «404» в Самаре было очень здорово. И уровень докладов, и развлекательная программа, и люди! Увидел старых друзей и знакомых — Илью Бирмана, Вадима Макеева, Сергея Чикуёнка, ребят из «knock knock» и многих других. Приобрёл новых знакомых и, как я надеюсь, друзей.

Вообще, сейчас
Сайту скоро 10 лет, его посещают в день от 2 до 5 тысяч человек (в зависимости от дня недели), читают чуть меньше 90 тысяч человек в месяц (если верить статистике моего хостера).
И вот, я не придумал ничего лучше, чем испортить всем этим людям ко сроку жизнь — сменить дизайн сайта. Что получается, мне нравится, очень похоже на блог Ильи и сайт Тани Мисютиной.
Похоже по двум причинам:
P.S. Нашёл фото где я спрашиваю Вадима когда «Опера» будет поддерживать дробные проценты.
PHP, UTF-8, всё ещё строки: упрощаем задачу, часть I
Итак, я достаточно долго думал над задачей как мне заменить все операции получения символа из строки по индексу, чтобы понять насколько она сложна. Решить её можно, но скучно и не нужно. Я подумал, что очень вряд ли у меня в коде встречаются все возможные случаи, которые я рассмотрел в прошлый раз, поэтому я решил посмотреть что же у меня встречается на самом делом.
Около 3,5 тысяч. Я временами дьявольски усидчив, но рассматривать каждую строку из нескольких тысяч, это для меня перебор. Что же делать? Для начала мне хочется упростить своё регулярное выражение. Конечно, по синтаксису PHP имена переменных можно писать, например, Безусловно, моё регулярное выражение захватило очень много лишнего, надо эти ситуации обработать. Например, можно исключить добавление элемента в массив, это легко узнаваемая конструкция ($arr[] = $elm): Ситуация мало изменилась, давайте попробуем улучшить её значительно. Например, удалить все строки, где происходит только явное обращение к массиву: $arr[’index’] (но не $arr[’index’][0]): Ощутимая разница. Открываем получившийся файл и смотрим что бы мы ещё могли выкинуть… Продолжение следует, я сегодня рано встал, пойду домой.Что же дальше?
bolk@dev:~/daproject$ find \( -name '*.php' -or -name '*.inc' -or -name '*.tpl' \) -exec \
egrep '\$\{?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*[\[\{]' -- {} \; |
sort -u | tee out-vars-1.log | wc -l
3670bolk@wiki:~/daproject$ IFS='\n'
for line in $(cat out-vars-1.log); do
(echo $line | sed -r 's/\$\w+\[\]//g' | egrep -q '\$\{?\w+[\[\{]') && echo $line
done | sed -r 's/^\s+//' | awk '!t[$0]++' | tee out-vars-2.log | wc -l
3357bolk@dev:~/daproject$ IFS='\n'
for line in $(cat out-vars-2.log); do
(echo $line | sed -r 's/\$\w+[\[\{](["'\''])[^\1]*\1(\]|\})([^\[\{])/\2/g' | egrep -q '\$\{?\w+[\[\{]') && echo $line
done | tee out-vars-3.log | wc -l
1353
Бьёрн Страуструп в России
Вернулся с семинара, было интересно, но организация — полный кошмар. Официальное начало в 8:30, Бьёрн получил доступ к микрофону примерно в 10, поесть было негде, за исключением крошечного, сразу забившегося буфета, микрофоны желающим задать вопрос приносили с большим опозданием или вообще не приносили, ноутбук для демонстраций слайдов несколько раз «залипал».
Но самое главное — синхронный перевод. О, я получил удовольствие. Избранные фразы из моего твиттера:
Переводчик на семинаре Страуструпа несёт такой бред,что я не знаю что интереснее — смысл доклада или горячный бред в наушнике.
Переводчик: «когда мы своппируем поинтеры…»
Ещё: «для начала я тайпирую аллокаторы…»
Бьёрн рассказывает про сборщик мусора (garbage collector), переводчик долго молчит, потом: «тут у нас никакого мусора».
«Что касается магнитуды… величины фолт толерантности» — бубнит в наушник переводчик.
«Чё-то по поводу исключений…ля-ля, тополя». Ну, переводчик в ударе.«У нас есть сингллинкед и анордеред массивы»,.. Даль вертится в гробу.
Бьёрн: «ок…», заметьте насколько переводчик более поэтичен: «да, давайте я соберусь с мыслями».
Переводчик переводит вопрос из зала: «ваша революция была давно, а родите нам ещё
чё-нить, до того как умрёте».Бьёрн (компьютер со слайдами опять сломался): «I don’t belive it», переводчик: «какого х…»
Бьёрн: «I think so…», переводчик, сквозь зевоту: «ага, щас попробую врубиться»
Переводчик, в сердцах: «о чём речь, вообще?!»
Бьёрн Страуструп
Иду на семинар Бьёрна Страуструпа (это автор Си++ и первый «компьютерный учёный»). Жестокий мир — начало в 7:30! В интернете никого нет, вы что, спите все что ли?
Мощь SVG и media queries
Вадим Макеев демонстрирует сейчас на фестивале «404» мощь SVG — картинка сама (через CSS media queries) изменяется для разных разрешений экрана, при масштабировании полноценное лого с текстом ужимается, текст сначала переносится, потом исчезает и остаётся один логотип.
Ребята, о таком применении SVG я даже и не думал, это очень круто.
Другой пример — внутри SVG выбирает один из внедрённых растров, опять же в зависимости от разрешения экрана (а можно, кстати, показывать специальную
Дополнено позднее: Вадим выложил слайды своего доклада (смотреть надо в «Опере»).
PHP, UTF-8, рассуждения по поводу строк

Работаю с низким приоритетом над автоматической заменой операций получения символа в строке по индексу на такую же операцию, но которая умела бы работать
В общем, задача сложная, я над ней думаю. Пока в сторону парсеров синтаксиса (colorer, Text_Highlighter и PHP tokinizer). Из исследования выяснилось, что все они могут мне помочь примерно в равной степени, так что я думаю, что возьму PHP tokinizer. К сожалению, ни один из парсеров не парсит PHP настолько удобно, чтобы избавить меня от всех проблем, так что я думаю над их решением. Когда у меня будет понимание как бороться со всеми проблемами, буду программировать.Что дальше?
Огрызки от собеседований
От собеседований, которые я недавно проводил, у меня осталась масса листочков с заковыристыми вопросами. Например, если человек говорил, что знает PHP на 10 из 10, то я ему давал, например, такой тест:
var_dump((bool) print_r(${strrev('SLABOLG')} = 1, $GLOBALS[1]{1} == (unset) b"1") == "00");Сказать есть ли тут ошибки, если нет, то что получим и что вообще тут происходит.
P.S. Я в Самаре, «Holiday Inn» отличный отель.