Вот бы CUDA в интерпретаторы

Было бы любопытно попытаться использовать GPU в интерпретаторах. В том же самом JavaScript (про который я всё говорил, что это самый недооценённый язык и который грозится перейти в разряд переоценённых) или PHP.

Вот алгоритм сортировки приспособили для выполнения на GPU, на NVIDIA GeForce GTX480, код его реализующий, позволяет сортировать аж несколько миллиардов (!) 32-хразрядных чисел в секунду. Центральным процессорам пока до этого результата далеко.

Правда, указанная видеокарта тот ещё монстр, в стоечный сервер её не очень-то засунешь, но это, верится мне, пока. Впрочем, карту можно взять и поскромнее, и загрузить работой, чего ей простаивать, она всё равно в сервере ничего не делает.

2 комментария

Очередной BSOD

BSOD в торговом центре (42.83КиБ)

Очередной BSOD в торговом центре. Всё-таки как я так и не привык видеть Windows в таких местах. Отчего-то всё ещё ощущение, что там должно быть что-то простенькое, хотя, на самом-то деле, самое простенькое сейчас — поставить полноценную OS и не мучиться.

P.S. Начал смотреть «Обмани меня», нравится.

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

DE██

DE██ (16.89КиБ)

Досматривая 13-ю серию первого сезона сериала «Обмани меня», обратил внимание на ноутбук исламского террориста — явно Latitude серии XFR, но две последние буквы в названии чем-то заклеены (кстати, на крышке, которую показывают мельком, название не заклеено).

Я где-то читал, что в авиакомпании не очень приветствуют упоминание своих брендов в фильмах-катастрофах, тут, вероятно, то же самое — логотип известной фирмы на ноуте, который использует террорист… Боятся, что возникнут нежелательные ассоциации.

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

«Жгут кислород»

Если мне кто-нибудь ещё хоть раз скажет, что обогреватели «жгут кислород», того буду нещадно высмеивать. Горение (когда что-то жгут, что-то горит) — это реакция окисления. В норме вокруг обогревателя нет другого окислителя, кроме кислорода, поэтому получается, что «жгут кислород» — это реакция окисления кислородом кислорода.

Не, ну конечно, из O2 можно получить O3, но условия совсем не те, нужен разряд. Может имеется ввиду окисление чего-то ещё? Но азот окисляется очень плохо, ну и воду в воздухе не очень-то окислишь до перекиси.

Могла бы окисляться сама нить, но чтобы «сжигать кислород» нити бы требовались дикие количества, между тем она служит годами. Да и вольфрам, из которого делают обычно нити накаливания, очень стойкий металл.

Все неприятные эффекты от обогревателей связаны не с глупым мифом о «сжигании кислорода», а с двумя причинами — иссушением воздуха (который уносится с тёплым воздухом из помещения, холодный несёт меньше паров) и сжиганием воздушной пыли (в случае открытой нити или наличия очень горячих зон), которая какую только гадость в себе не несёт.

А то смотрите какую ерунду пишут:

«Иколайн» сжигает кислород в помещении?

Температура рабочей поверхности пластины обогревателя «Иколайн» невысока — всего 250°С, что недостаточно для сжигания кислорода в помещении. Исключение составляют обогреватели ЭИ10R, которые являются высокотемпературными, но они применяются только на открытых площадках

Кстати, теперь мы знаем при какой температуре якобы «сжигается кислород» — рабочая температура ЭИ10R составляет 600°C. Что за реакция всё-таки может иметься ввиду (которая при 250° не идёт, но при 600 — пожалуйста) я так и не смог понять.

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

«Жгут кислород»—2

Не поленился написать на сайт, с которого (в прошлом посте) цитировал абзац из FAQ про «сжигание кислорода». Вот что ответили:

Взрослый человек должен понимать, что основная масса населения не осведомлена о таких понятиях как «сжигает кислород» или «не сжигает кислород», но при приобретении климатического оборудования активно интересуется данным вопросом. Объяснять, что такого понятия не существует, а на поверхности просто сгорает пыль и т. п. с выделением запаха — бесполезное занятие, клиент уйдет туда, где скажут «не сжигает кислород».

Полагаю вы хотели сделать открытие, основываясь на материалах моего сайта? Должен вас разочаровать, «сжигание кислорода» не происходит. Надеюсь Вы меня поняли, а я Вас ;)

Правда, остаётся открытым вопрос почему же ЭИ10R всё-таки его «сжигает».

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

Квартира строится

Квартира (47.24КиБ)

Прошло два месяца и дом, где мы купили квартиру, строится приятными темпами — как видно на фото, построили два этажа подземной парковки и приступают к строительству первого этажа здания. Между делом, стоимость жилья в этом квартале сильно выросла — примерно тысяч на шесть.

Из проблем — я до сих пор не получил полис страхования. В прошлый раз я нашёл в нём ошибку — улицу прописки написали «Зоре», а не «Зорге» (это фамилия советского разведчика), так и переделывают до сих пор.

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

Некоторые программисты стыдятся знания PHP

Просто наблюдение. Заметил, что на собеседовании программисты оправдываются, что знают PHP. «Не знаю как так получилось».

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

Клички городов

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

Есть несколько обсуждений на эту тему (например, на SearchEngines), но никакого подробного исследования в духе Лебедева.

Мне больше всего нравится «три шурупа» — неофициальное название Уфы («ӨФӨ» — так пишется название столицы Башкортостана на башкирском).

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

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

Надоело уменьшать картинки и скруглять им углы вручную. Написал программу для этого. Она уменьшает все JPEG-файлы (с расширением jpg) в текущем директории, скугляет им углы и пропускает их через встроенный оптимизатор JPEG-файлов. Считается, что фон белый.

Обработанные файлы попадают в директорий «out», который создаётся по текущему пути.

Параметры запуска: makecorner [качество [ширина [радиус]]]

По-умолчанию: качество=85, ширина=660, радиус=10.

P.S. JavaScript-библиотеки я тоже посмотрел, не понравились. У всех есть определённые недостатки, кроме того, в RSS картинки будут без скругления.

P.P.S. Размер файла связан с тем, что там внутри библиотека GD и jpegtran (для оптимизации JPEG).

Добавлено через день: ключи запуска программы изменились.

24 комментария

Симферополь

Напоминаю, что читающие трансляции (в ЖЖ, «ярушке», ещё где-то) не увидят галерей картинок.

Симферополь — город средних размеров (около 340 тысяч жителей) в республике Крым (Украина).

Я ездил туда по работе, поэтому удалось прогуляться только в центре города. А центр — это, обычно, не совсем то место, которое характеризует город. Вообще-то в Симферополе я уже второй раз, но в первый был проездом. Всё, что тогда я успел увидеть — это старые чехословацкие троллейбусы, которые на ходу уже полвека.

Жёлто-красный троллейбус движется по городской улице, на заднем плане — здания с вывесками «ТОРГ» и «ОКНА», деревья и серый автомобиль.
Солнечная городская улица с троллейбусом, легковым автомобилем, пешеходами, цветочными клумбами и вывеской «ГАЛЕРЕЯ СВІТЛА» на фоне зданий и деревьев.
Белый троллейбус с красной полосой и рекламой «Saturn» движется по городской улице под проводами; на заднем плане — многоэтажный дом с вывеской «АПТЕКА», автобус и пешеходы.

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

Улица с историческими зданиями, на стене — табличка «Место для курения», деревья, машины и фонарный столб.
Панорама двора с многоэтажным жилым домом, высокими зелёными деревьями, цветущим кустарником и белой будкой охранника на фоне ясного голубого неба.
Угловой трёхэтажный дом в стиле модерн с балконами и магазинами на первом этаже, пешеходы и автомобиль на мощёной площади под ясным небом.
Пешеходная улица с брусчаткой, старинными зданиями, деревьями и людьми; на переднем плане — красный автомобиль слева и мужчина в синей рубашке справа.

Если завязать вам глаза, долго куда-то везти, а потом высадить в центре Симферополя, может показаться, что вы в деревне. Нет, куры и козы не ходят по улицам города, но за фасадами зданий — внутренние дворики, почти неотличимые от деревенских. Внутренние маленькие мирки, где лают собаки на цепи и все здороваются со всеми.

Узкая улица между одноэтажными зданиями с видом на высокую белую башню с арочными окнами; на дороге припаркован автомобиль, летят голуби.
Узкая асфальтированная дорожка между старыми зданиями с облупившейся штукатуркой, деревянными окнами и воротами, увитыми зеленью, под ярким солнцем.
Открытая дверь дома под деревянным навесом, рядом стол с книгами и горшки с растениями, вид во двор через металлическую калитку с волнистым узором.
Узкая грунтовая дорожка между старым зданием с облупившейся штукатуркой и густыми зелёными кустами, освещённая ярким солнцем, отбрасывающим тени на землю.
Металлическая калитка с сеткой и массивной дверцей, частично скрытая густой зелёной лианой, растущей на каменной стене; слева видна часть зелёного строения.
Дворик между старыми домами с металлической лестницей, развешанным бельём на верёвках и зелёными растениями на переднем плане под ясным голубым небом.
Тропинка между каменной стеной и старым зданием с облупившейся штукатуркой и ржавыми трубами, уходящая к маленькому домику в тени деревьев.

Наверное, именно этим миркам город обязан давно забытому в Москве явлению — маленькие дети, передвигающиеся по городу (не двору!) самостоятельно. Мой маршрут примерно на километр совпал с маршрутом детей на фото, дальше они пошли по своим делам, а я свернул

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

На взгляд туриста, это мирный, спокойный и очень зелёный город. Дорогу можно переходить не торопясь, кое-где вообще машины проезжают очень редко, и это в разгар дня! В Симферополе хорошо, спокойно, только очень мало освещения — в центре есть целые улицы, где очень темно ночью. Даже обветшалые дома, покосившиеся заборы, с облупившейся краской, смотрятся почему-то органично, наверное из-за зелени, она много скрадывает.

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

Городская площадь с фонтаном, где люди отдыхают на скамейках и гуляют среди деревьев и цветников; на фоне — здания, включая здание с вывеской «Сбербанк».
Солнечная пешеходная улица с каменной мостовой, деревьями, цветочными клумбами и людьми; видны здания, фонари и знак «Банкомат».
Красный автомобиль Chery с номером 788 ВС припаркован на тротуаре под деревьями, на заднем плане видны пешеходы и белая машина.
Тенистая улица с рядами деревьев, старинным зданием с арочными окнами, припаркованным автомобилем, информационным стендом и дорожными знаками на солнечном фоне.
Солнечный свет пробивается сквозь густую листву дерева с длинными стручками, освещая фрагмент фасада здания с деревянными дверями на заднем плане.
Тротуар вдоль здания с вывеской «АВИАПЕРЕВОЗКИ» и кондиционерами, рядом — ряд деревьев с зелёной листвой, на заднем плане видна дорога с автомобилем.
Зелёный кустарник и лианы покрывают скалу на фоне леса, освещённого солнечным светом.
Вид на крыши домов и густую зелень деревьев под ясным голубым небом, через которое проходит один электрический провод.

Последнее, где я побывал — аэропорт. В Симферополе он маленький, до самолёта нужно идти пешком, очень непривычно, хотя и разумно. В Казани аэропорт тоже небольшой, но пассажиров важно развозят на автобусе.

Пассажиры с багажом идут по перрону к самолёту, к которому подведён трап с надписью «SIMFEROPOL», на фоне ясного неба.
Двое сотрудников в белых рубашках проверяют пассажиров у рамки металлоискателя в зале с голубыми стенами, на полу — сумки и корзины.
В зале ожидания аэропорта пассажиры сидят на жёлтых стульях; на табло — номер рейса 065, на стене — рекламный плакат с надписью «Крым Болгария».

В целом, очень приятный город, по крайней мере его центр. Я не походил по туристическим местам, мне не хотелось, но надеюсь в каком-нибудь солнечном месяце ещё вернуться и посмотреть на пятисотлетний дуб «Богатырь Тавриды» (окружность — 6 метров), это любопытно.

Остальные фотографии можно увидеть на «Яндекс.Фотках».

22 комментария

«Декстер»

Вчера мы досмотрели «Теорию лжи» и начали смотреть «Декстер». Ребята, я не понимаю чем интересен этот сериал. Объясните, скука же.

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

Пакетное скругление картинок v0.1

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

Я постарался выполнить всё то, что смог добавить за вечер.

Во-первых, изменился способ запуска программы, теперь у программы есть ключи. Их имена и значения выводятся при запуске программы (если ключи не указать, то используются их значения по-умолчанию):

-​-quality=85 -​-radius=10 —background=#ffffff -​-mask=*.{j,J}{p,P}{g,G} -​-out-dir=out

Во-вторых, у программа может использовать файл установок (если он существует), там перечисляются все те же ключи (только без двух минусов) в формате INI-файла, файл должен называться «makecorner.ini».

GUI за вечер, конечно же, я не прикручу, а с сохранением EXIF есть сложности: на деле оказалось, что EXIF не то вырезается, он просто не сохраняется библиотекой GD, буду думать.

Скачать новую версию можно там же, где и предыдущую.

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

Ошибки до собеседования

Некоторые люди умудряются совершать ошибки ещё до собеседования. А именно — осложнить мне чтение своего резюме. Соответственно, из двух ребят я буду сначала собеседовать того, чьё резюме мне удалось прочитать без мучений. Загвоздка в том, что до второго претендента дело может и не дойти.

Ошибок такого типа пока обнаружено ровно две: резюме в загадочных форматах (например, ODT — это формат OpenOffice, у меня есть великолепный MS Office, а ставить OO только ради того чтобы прочесть чьё-то резюме, извините…) и резюме на английском.

Насчёт первого небольшая ремарка — прежде чем слать резюме в ODT или WPS, подумайте, теряете ли вы что-то, если отошлёте его просто текстовым файлом.

О втором подробнее. Я не люблю читать на английском, просто потому что мне труднее его читать, чем русский. Если нет положительной обратной связи (что-то прочёл, получил какие-то блага, полезную мне информацию, например), я стараюсь не читать на английском. Поэтому я старательно подкрепляю суждение, что английского я не знаю (что, кстати, правда — в моей системе верований «знать язык» означает «уметь на нём сочинять стихи», такого же качества, как на родном).

Нужно понимать, что для человека идти по пути наименьшего сопротивления естественно, если только другой путь не принесёт очевидных благ (признание, славу, деньги, внутреннее равновесие и так далее — как мы видим, даже внутренняя мотивация сгодится). В чтении резюме таких выгод нет. Так что я сначала обработаю заявки тех людей, чтение резюме которых затратит меньше моих сил, а только потом (это же работа, её всё равно делать) — остальных.

В результате, как я уже говорил, до «остальных» дело может и не дойти — достойный кандидат может найтись раньше.

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

Сколько глаз у семи нянек?

Я часто говорю, что многие взрослые недалеко от детей ушли по интеллектуальному развитию. Развитие на одном уровне, но по разным причинам. Простая загадка — на вас смотрят десять человеческих глаз, сколько людей перед вами?

Дети, не задумываясь, почти всегда ответят «пять», потому что в их опыте по-другому быть не может, а они слабо умеют синтезировать, многие взрослые станут вспоминать какую-то формулу, которую, якобы, когда-то учили в ВУЗе и, не вспомнив, успокоятся.

Правильный ответ же формул не требует — от одного нуля до бесконечности. Нужно только включить мозг.

Добавлено позднее: почему-то многие восприняли это как загадку. На деле это иллюстрация.

43 комментария

Ближайшие поездки

C 10-го сентября по 12-е сентября — Екатеринбург. С 22-го сентября по 24-е — Ярославль. Октябрь — возможно Самара (под вопросом). С 3-го ноября по 6-е — Казань.

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

PHP и UTF-8

Все уже на UTF-8, а PHP как-то не успел запрыгнуть на поезд. Я сейчас исследую тему перевода одного большого веб-сервиса на UTF-8, а он, так случилось, написан на PHP.

Увы, с UTF-8 в PHP всё очень тяжело. «Голый» PHP c UTF-8 работать, можно сказать, не умеет. Положение несколько скрашивает модуль Multibyte String, особенно тем фактом, что он умеет заменять некоторые функции на их Unicode-аналоги автоматически. Увы, не для всех строковых функций PHP в этом модуле есть аналоги, например, для strspn его не существует. Таких функций, увы, больше, чем хотелось быть.

Немного праздника в этот однобайтный склеп привносит тот замечательный факт, что UTF-8, в общем-то, придумали неглупые люди — по виду байта всегда можно отличить где мы — в начале последовательности символа или нет. Это позволяет таким функциям, как str_replace или strtr (когда вызывается с двумя параметрами) работать и с UTF-8 тоже.

Печальнее всего с двумя вещами.

Во-первых, PHP самой распоследней версии всегда считает, что строки содержат символы размером в один байт, на практике это значит, что $str[$index] вернёт на не $index-й символ, а $index-й байт последовательности символов, а это существенная разница. Найти все такие места в коде и не перепутать их с адресацией к массиву — задача непростая. Я придумал для этой цели использовать специальную функцию, которая, если на вход ей подаётся массив, работает с ним правильно, тихонько логгируя все такие вызовы.

Вторая проблема заковыристее. В PHP испокон веков аж три модуля для работы с регулярными выражениями — PCRE, Regular Expression (POSIX Extended) и уже знакомый нам Multibyte String. Первый работает с Perl-совместимыми регулярными выражениями, оставшиеся — с POSIX-синтаксисом, причём «Multibyte String» содержит UTF-8-аналоги всех функций из «Regular Expression (POSIX Extended)».

PCRE в таких помощниках не нуждается, потому что сам умеет работать с UTF-8, у него даже ключ специальный есть — «u» («Unicode»). Это в теории. На практике код

if (preg_match('/(\w)/iu', 'ПриветN', $m)) {
        var_dump($m);
    }

Выдаст нам… букву «N». Почему? Находить в строке UTF-8-символы куда сложнее, чем символы размером в один байт, поэтому для производительности разработчики решили, что… Впрочем, слово разработчикам:

Matching characters by Unicode property is not fast, because PCRE has to search a structure that contains data for over fifteen thousand characters. That is why the traditional escape sequences such as \d and  \w do not use Unicode properties in PCRE

То есть «\d» (цифры), «\w» (буквы) и, кстати, «\b» (граница слова) и «\s» (пробельные символы) работают только с однобайтовыми кодировками. Ужасно? Не то слово! Всё потеряно? К счастью, нет.

Вообще-то в PCRE есть специальные последовательности для различных классов Unicode-символов, например, «\p{L}» — это буквы, «\p{N}» — цифры и так далее. Можно заменить все не-Unicode последовательности этими аналогами. Но и это достаточно тяжело, особенно на тех объёмах, с которыми я имею дело. Простым поиском с заменой не обойтись — «[\w\s]» должен раскрываться уже иначе, не так же как «(\w|\s)».

Если внимательно читать документацию по PCRE, то найдётся много интересного, в том числе решение и этой проблемы. В одной из версий этой библиотеки появилась экспериментальная конструкция, называемая «глаголом» (VERB), «глаголы» умеют очень многое, но нас интересует специфический «глагол» «UCP», который (ура) занимается тем, что переключает «\w», «\s», «\d» и «\b» в полностью юникодный режим.

То есть такой код даст нам ожидаемый результат:

if (preg_match('/(*UCP)(\w)/iu', 'ПриветN', $m)) {
        var_dump($m);
    }

Победа? Ничего подобного. Этот глагол работает начиная с PCRE 8.10, в самой последней стабильной версии PHP (5.3.3) содержится PCRE 8.02, там этот глагол ещё не поддерживается, я проверял.

Нужная нам версия 8.10 войдёт в PHP 5.3.4. Но, во-первых, её ещё нужно дождаться (пока нет даже кандидата в релизы), во-вторых, 5.3 — это совсем другая ветка, ещё пока недостаточно стабильная, я как-то уже упоминал, что пара попыток перевести кое-какие проекты на PHP 5.3 закончились провалом, причём из-за багов этой версии PHP.

Но не стоит опускать руки, 4-5 лет назад я уже сталкивался с подобной ситуацией и помню, что при компиляции PHP есть возможность указать внешнюю PCRE-библиотеку (ключ «—with-pcre-regex=DIR»), что полностью решает проблему, я вчера проверил.

Подведём итоги. Для перевода (большого) проекта на UTF-8 необходимо найти все функции, которые обращаются к строкам, заменить их на Unicode-аналоги (если они есть в модуле Multibyte String, то оттуда, если их там нет — на аналоги, написанные на PHP), функции implode, explode, str_replace, strtok (возможно, какие-то ещё) в аналогах не нуждаются, операции $str[$index] заменить на функцию, которая умеет работать и с Unicode-строками и с массивами, вкомпилить в PHP PCRE версии 8.10 и в регулярных выражениях добавить ключ «u» и «глагол» «UCP».

Ну а так же не забыть про внешние источники данных — перевести базу на UTF-8, сконвертировать шаблоны, комментарии в коде и тому подобные места.

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

PHP и UTF-8: первый этап

Я решил описать переход проекта на UTF-8, как это у меня получается. Иногда будут достаточно больше куски кода. На предыдущем («нулевом») этапе я систематизировал все знания, которые получил на этапе исследования. На этом этапе я хочу посмотреть сколько же функций нужно будет переписать. Из всего богатства PHP проект использует далеко не все функции, так что нет никакого смысла переписывать их все.

Основная идея — я добавлю в проект статический класс UTF. В этом классе будет переключатель (я назвал его «ON»), если он в состоянии «false», то все статические методы класса вызывают функции, работающие с однобайтовыми кодировками, иначе — с UTF-8.

Выглядит он примерно так:

class UTF
{
    const ON = false;

    static public function strpos($haystack, $needle, $offset = 0)
    {
        if (self::ON) {
            return mb_strpos($haystack, $needle, $offset, 'UTF-8');
        }

        return strpos($haystack, $needle, $offset);
    }

    // … и так далее
}

Для чего нужен переключатель ON? Он позволяет переключить разом весь проект в состояние «UTF-8», посмотреть что не заработало и откатиться, если нужно. Кроме того, это позволяет заменять функции даже в том (моём) случае, если с проектом работают другие люди — пока переключатель выключен, остальные члены команды не заметят изменений.

Итак, нам надо посмотреть что же нужно переписывать и заменять в проекте. Я поступил просто — открыл в руководстве по PHP разделы строковых функций, функций PCRE и регулярных выражений в формате POSIX, скопировал их себе в программу и оформил как регулярное выражение:

<?
$funclist = <<<LIST
addcslashes — Quote string with slashes in a C style
chop — Alias of rtrim
chr — Return a specific character
chunk_split — Split a string into smaller chunks
convert_cyr_string — Convert from one Cyrillic character set to another
count_chars — Return information about characters used in a string
hebrev — Convert logical Hebrew text to visual text
hebrevc — Convert logical Hebrew text to visual text with newline conversion
html_entity_decode — Convert all HTML entities to their applicable characters
htmlentities — Convert all applicable characters to HTML entities
htmlspecialchars_decode — Convert special HTML entities back to characters
htmlspecialchars — Convert special characters to HTML entities
lcfirst — Make a string`s first character lowercase
levenshtein — Calculate Levenshtein distance between two strings
localeconv — Get numeric formatting information
ltrim — Strip whitespace (or other characters) from the beginning of a string
metaphone — Calculate the metaphone key of a string
money_format — Formats a number as a currency string
nl_langinfo — Query language and locale information
ord — Return ASCII value of character
rtrim — Strip whitespace (or other characters) from the end of a string
setlocale — Set locale information
similar_text — Calculate the similarity between two strings
soundex — Calculate the soundex key of a string
sscanf — Parses input from a string according to a format
str_getcsv — Parse a CSV string into an array
str_ireplace — Case-insensitive version of str_replace.
str_pad — Pad a string to a certain length with another string
str_rot13 — Perform the rot13 transform on a string
str_shuffle — Randomly shuffles a string
str_split — Convert a string to an array
str_word_count — Return information about words used in a string
strcasecmp — Binary safe case-insensitive string comparison
strcmp — Binary safe string comparison
strcoll — Locale based string comparison
strcspn — Find length of initial segment not matching mask
stripcslashes — Un-quote string quoted with addcslashes
stripos — Find position of first occurrence of a case-insensitive string
stristr — Case-insensitive strstr
strlen — Get string length
strnatcasecmp — Case insensitive string comparisons using a "natural order" algorithm
strnatcmp — String comparisons using a "natural order" algorithm
strncasecmp — Binary safe case-insensitive string comparison of the first n characters
strncmp — Binary safe string comparison of the first n characters
strpbrk — Search a string for any of a set of characters
strpos — Find position of first occurrence of a string
strrchr — Find the last occurrence of a character in a string
strrev — Reverse a string
strripos — Find position of last occurrence of a case-insensitive string in a string
strrpos — Find position of last occurrence of a char in a string
strspn — Finds the length of the first segment of a string consisting entirely of characters contained within a given mask.
strtolower — Make a string lowercase
strtoupper — Make a string uppercase
strtr — Translate certain characters
substr_compare — Binary safe comparison of two strings from an offset, up to length characters
substr_count — Count the number of substring occurrences
substr_replace — Replace text within a portion of a string
substr — Return part of a string
trim — Strip whitespace (or other characters) from the beginning and end of a string
ucfirst — Make a string`s first character uppercase
ucwords — Uppercase the first character of each word in a string
wordwrap — Wraps a string to a given number of characters
preg_filter — Perform a regular expression search and replace
preg_grep — Return array entries that match the pattern
preg_last_error — Returns the error code of the last PCRE regex execution
preg_match_all — Perform a global regular expression match
preg_match — Perform a regular expression match
preg_quote — Quote regular expression characters
preg_replace_callback — Perform a regular expression search and replace using a callback
preg_replace — Perform a regular expression search and replace
preg_split — Split string by a regular expression
ereg_replace — Replace regular expression
ereg — Regular expression match
eregi_replace — Replace regular expression case insensitive
eregi — Case insensitive regular expression match
split — Split string into array by regular expression
spliti — Split string into array by regular expression case insensitive
sql_regcase — Make regular expression for case insensitive match
ctype_alnum — Check for alphanumeric character(s)
ctype_alpha — Check for alphabetic character(s)
ctype_cntrl — Check for control character(s)
ctype_digit — Check for numeric character(s)
ctype_graph — Check for any printable character(s) except space
ctype_lower — Check for lowercase character(s)
ctype_print — Check for printable character(s)
ctype_punct — Check for any printable character which is not whitespace or an alphanumeric character
ctype_space — Check for whitespace character(s)
ctype_upper — Check for uppercase character(s)
array_change_key_case — Changes all keys in an array
array_multisort — Sort multiple or multi-dimensional arrays
arsort — Sort an array in reverse order and maintain index association
asort — Sort an array and maintain index association
krsort — Sort an array by key in reverse order
ksort — Sort an array by key
natcasesort — Sort an array using a case insensitive "natural order" algorithm
natsort — Sort an array using a "natural order" algorithm
rsort — Sort an array in reverse order
sort — Sort an array
LIST;

$funcs = array();

foreach (explode("\n", $funclist) as $line) {
    list($line) = explode(" ", $line, 2);

    $funcs[] = $line;
}

$regexp = '/(?<!UTF::)\b(' . implode('|', $funcs) . ')\s*\(/si';

set_time_limit(0);

$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('.'));

for (;$it->valid(); $it->next()) {
    $ext = pathinfo($it->getBaseName(), PATHINFO_EXTENSION);

    if ($ext == 'php') {
        if (preg_match_all($regexp, file_get_contents($it->key()), $matches)) {
            echo $it->key(), "\n";

            echo '    ', implode("\n    ", array_map('strtolower', $matches[1])), "\n\n";
        }
    }
}

Список функций я проредил — удалил оттуда те, у которых проблем с UTF-8 нет (str_replace, explode, md5 и прочие), после запуска у меня получился список файлов с которым придётся работать и список функций, которые надо заменять (то есть те, которые должны быть описаны в классе UTF):

bolk@dev:~/daproject$ grep ' ' tool.log | sort | uniq -c | sort -rn
    225     substr
    195     preg_replace
    174     trim
    132     preg_match
    132     htmlspecialchars
    127     strpos
    113     strtolower
     88     strlen
     50     preg_replace_callback
     40     rtrim
      …

bolk@dev:~/daproject$ grep './' tool.log
./tool.php
./htdocs/_file.php
./htdocs/file1.php
./htdocs/dir/file1.php
./htdocs/dir/file2.php
./htdocs/dir/file3.php
./htdocs/dir/file4.php
./htdocs/dir/file5.php
./htdocs/class.php
./htdocs/_ajax.php
…

На следующем этапе я заменю эти функции на их аналоги. Кстати, если у вас используются оба алиаса одной функции (например, chop и trim), то подумайте над тем, чтобы привести всё к единому виду.

Отдельно надо будет что-то решать с файловыми функциями — они работают с байтами, а не с символами UTF-8, пока я откладываю их в сторону, попробую решить это позже.

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

2 комментария

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

Итак, на первом этапе перевода нашего веб-сайта на UTF8 мы выяснили какие функции нам нужно будет переписать и какие файлы затронуть. Например, у меня это 38 функций и 173 файла. На втором этапе я предлагаю выяснить не могли ли мы что-то забыть.

Могли. Во-первых, я ищу только файлы с расширением «php», даже если проект ваш и вы твёрдо помните, что никаких других расширений для PHP вы не используете, то я всё равно рекомендую это проверить — может есть файлы с расширением «inc» в чужой библиотеке или где-то в шаблонах используется PHP?

Итак, как их найти? Мне кажется, хороший путь — посмотреть где встречается сигнатура „<?“:

bolk@dev:~/daproject$ grep --binary-files=without-match -R '<?' * | cut -d':' -f1 |
> grep -v '.svn' | sed -re 's/.*?/([^/]+)/1/' | awk -F. '/./{print $NF}' | sort -u
conf
csv
example
inc
js
package
php
pm
png
py
svg
tpl
xml
xsl

Очевидно, что файлы формата XML (у меня это расширения xml, xsl и svg) так же могут содержать сигнатуру „<?“ (там это часть языка), текстовые файлы (conf, csv) так же её могут содержать (они вообще могут содержать что угодно).

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

bolk@dev:~/daproject$ grep --binary-files=without-match -R '<?' * | cut -d':' -f1 | egrep -v '.svn|.php' |
> xargs -rn1 -I{} php -r 'echo `php -l {}`;' | grep -F 'No syntax errors detected in' | sort -u
No syntax errors detected in htdocs/_dir/file1.csv
No syntax errors detected in htdocs/_dir/file2.png
No syntax errors detected in htdocs/_dir/file3.csv
No syntax errors detected in htdocs/_dir/file4.png
No syntax errors detected in htdocs/_dir/file5.csv
No syntax errors detected in templates/file1.tpl
…

Оказывается, PHP у меня есть даже внутри файлов с расширением png!

К несчастью, у меня таких файлов достаточно прилично — 39, поэтому я собираюсь некоторые из них отбросить сразу:

sed -e 's/^No syntax errors detected in //' tool2.log | xargs -n1 head --verbose | less

Оказалось, что рассматривать как файлы PHP нужно файлы с ещё двумя расширениями — inc и tpl, остальные так же содержат PHP-код, но его там, либо одна строка (только вызов include), либо это файл примера (с расширением example).

Поэтому я добавлю в программу из предыдущего этапа ещё два расширения и запущу её снова. У меня количество функций не изменилось — 38, а вот файлов добавилось — стало 176 против 173. Ура, теперь файлы с PHP-содержимым мы не упустим.

А что по поводу используемых расширений PHP? Их много и с каждым месяцем становится всё больше (я сам написал 3 или 4), вдруг какие-то из них не работают с UTF-8?

Для начала неплохо было бы узнать сколько у нас вообще расширений. Я знаю, что расширения в PHP подключаются двумя способами: в конфигурационном файле и при помощи функции dl. Для начала посмотрим, используется ли у нас функция dl:

bolk@dev:~/daproject$ find \( -name '*.php' -or -name '*.inc' -or -name '*.tpl' \) -exec egrep -i 'dl\s*\(' {} \; -print
    * @return bool Success or not on the dl() call
            // if either returns true dl() will produce a FATAL error, stop that
            return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
./php/lib/PEAR.php
        if(!extension_loaded("mysql")) dl("mysql.so");
./php/db/mysql.php

У меня dl используется. Берём на заметку, что через dl подгружается модуль MySQL, а заглянув в PEAR, обнаружим, что там есть метод loadExtension, который является мультиплатформенной обёрткой для dl.

Для очистки совести я поискал случаи использования loadExtension в проекте (не забудьте, что регистр нужно игнорировать), но не нашёл.

Итак, мы выяснили какие модули у нас подгружаются через dl — только один (mysql), что же вкомпилированно в сам PHP или подключено из файлов конфигурации? Нет способа лучше, чем узнать что сам PHP думает об этом:

bolk@dev:~/daproject$ php -m
[PHP Modules]
zip
xmlwriter
libxml
xml
wddx
tokenizer
…

У меня это 54 модуля. Это очень много. Действительно ли их столько используется в проекте? Вот уж вряд ли. Надо каким-то образом найти все те, которые используются. Как это сделать? Вообще, модули могут быть использованы двояко — в виде функций этих модулей (и тогда нам надо найти вызовы этих функций), либо как объекты классов, предоставляемых модулем.

Отсюда мысль — узнать какие функции/классы содержатся в модуле и поискать их в файлах.

Давайте я это сделаю в следующей части, а то мне сейчас собеседование проводить.

2 комментария

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 комментарий

Тест на понимание

Кстати, мой текст про «Ошибки до собеседования» прекрасный тест на восприятие текста. В нём есть две сентенции, которые почему-то пропустила основная масса читателей.

Первое — резюме лучше слать как обычный текст, если возможно, вторая — резюме, которое почему-либо сложно прочитать, отправляется в конец очереди.

Неизвестно откуда читателями синтезировано два ложных утверждения: якобы я предлагаю слать резюме в формате DOC (это не так) и якобы я не рассматриваю резюме на английском или в каких-то форматах, кроме текстового (это не так).

Где вы это всё вычитали, м?

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

Как бороться с туннельным синдромом

В ЖЖ появилась отличная статья «RSI — как бороться с туннельным синдромом», полезна она тем, что вполне тянет на полное исследование вопроса. Крайне, крайне рекомендую к прочтению полностью, а себе я выпишу несколько вещей, которые мне показались особенно примечательными:

  • чтобы проверить предрасположенность к RSI, нужно попробовать оттянуть большой палец к запястью, если получается — есть предрасположенность (предрасположенность к RSI — это растянутость связок, растянутость связок побуждает кисть «болтаться» в работе, и приводит к износу сухожильной сумки)
  • алкоголь дополнительно повреждает нервы в воспалённом районе кисти и, похоже, смазку сухожилий
  • PowerBall нужно использовать без фанатизма. Регулярное превышение 6-8 тысяч оборотов на нём вам сделает RSI, если его даже не было. Для того, чтобы укрепить мышцы, надо систематично крутить его на относительно небольших оборотах: «столько, пока комфортно терпишь, плюс ещё тридцать секунд»
  • используйте клавиатуру с мягким ходом клавиш, без «дребезга» и щелчков. Шелчки передаются связкам и травмируют их
  • обрезайте ногти под ноль. Два миллиметра — уже много. Нажатие на клавиатуру подушечками пальцев щадит связки сильнее, чем нажатие или задевание части поверхности клавиши ногтями
  • мышку использовать нельзя. В списках причин RSI мышь стоит на первом месте. В опросниках у врачей по поводу RSI мышь стоит на первом месте: «Используете ли вы компьютерную мышку?» Используйте точпад

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

ЖЖ, OpenID и мой блог

Good news, everyone!

Я взял примеры, которые идут с библиотекой OpenID от JanRain, которую я использую. И оказалось, что OpenID ЖЖ в примерах работает нормально. Это означает, что это я с библиотекой делаю что-то не то. И значит это можно поправить. Буду править. Оказалось, что не все тесты работают нормально. Значит, надо искать ошибку в библиотеке. Ох.

Добавлено позднее: а пока написал в JanRain и ЖЖ по письму с полным описанием бага.

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

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

«Загадочная травма», о которой я писал меньше месяца назад отпустила. Как только боль прошла, в тот же день смог поднять «разогревочные» 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 уже подъём.

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

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

Веб-сервис оптимизации SVG

Веб-сервис оптимизации SVG (11.36КиБ)

Евгений Рыжков изготовил из моего скрипта оптимизации SVG веб-сервис. Скрипт я когда-то писал для книги и он появился после существенного развития идей Джоша Хирта, который показал принципиальную возможность автоматической оптимизации этого формата.

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

33

Ну вот мне и 33. В этой связи часто вспоминают выражение «возраст Христа», сделайте мне одолжение не надо в мой день рождения вспоминать во сколько Христос помер, мне не будет приятно.

Я как-то незаметно завёл традицию — каждый год в этот день я вспоминаю что я успел сделать с прошедшего дня рождения. Сегодняшний день не исключение, но в этом году будет ещё и фотоnew.

Мужчина в коричневой куртке и джинсах стоит в лесу, держа лопату рядом с вырытой ямой и молодым деревцем, готовясь к посадке.
Сажаю дерево

Согласно расхожей поговорке, настоящий мужчина должен посадить дерево, построить дом и вырастить сына. В этом году я начал претворение этого плана в жизнь. Итак, с предыдущего дня рождения я успел следующее:

33 комментария

PHP и UTF-8: четвёртый этап

В прошлой части мы кое-что заменили в коде, в этой части неплохо было бы поискать не забыли ли мы что-то ещё заменить. А это значит — куча работы руками.

Какие у нас есть варианты?
Какие у нас есть варианты? Функцию preg_replace_callback мы рассмотрели ещё в прошлый раз (а я это сделал сегодня, ничего криминального не нашёл), но есть и другие callback-функции, а так же eval, регулярные выражения с ключом «e» (PREG_REPLACE_EVAL), call_user_func, call_user_func_array и create_function.

Начнём с eval, его проще всего найти:

bolk@dev:~/daproject$ find -type f ( -name '*.php' -or -name '*.tpl' -or -name '*.inc' ) | xargs -n1 egrep -li 'bevals*('
./php/lib/magpierss/extlib/Snoopy.class.inc
./php/lib/PEAR.php
./php/dir1/file1.php
./php/dir1/file2.php
./php/dir1/file3.php

К счастью, почти всё что я нашёл — это eval-вызовы в коде на JavaScript внутри PHP-скриптов, так что править мне ничего не пришлось. В вызовах call_user_func, call_user_func_array и create_function я так же ничего криминального не нашёл (проверяются точно так же).

Теперь я попробую найти вообще все callback-функции. Это чуть сложнее сделать — в руководстве по PHP раздела, где они были бы перечислены нет. В chm-версии руководства я просто поискал строку «callback».

Не все callback-функции нас интересуют, нужны только те, которые возвращают строковый результат. Кроме того, нужно поискать использование констант, которые задают в некоторых вызовах callback-функции.

Вот что у меня получилось: readline_callback_handler_install, readline_callback_read_char, ob_start, session_set_save_handler, CURLOPT_HEADERFUNCTION, CURLOPT_PASSWDFUNCTION, CURLOPT_PROGRESSFUNCTION, CURLOPT_READFUNCTION, CURLOPT_WRITEFUNCTION, FILTER_CALLBACK, iterator_apply, set_exception_handler, pcntl_signal, array_map, array_filter, array_walk, array_walk_recursive, array_udiff_uassoc, array_diff_uassoc, array_udiff_assoc, array_diff_ukey, array_udiff , array_intersect_ukey, array_uintersect_uassoc, array_uintersect_assoc, array_intersect_uassoc, array_intersect_assoc , array_uintersect, array_reduce, uksort, usort, uasort.

Или для командной строки (регулярное выражение я тут подсократил):

bolk@dev:~/daproject$ find -type f ( -name '*.php' -or -name '*.tpl' -or -name '*.inc' ) |
> xargs -n1 egrep -H 'array_map|array_filter|array_walk|…|uasort' | less

В выводе нужно высматривать только вызовы строковых функций и статических методов, которые стоят на месте callback-параметров или переменные на тех же местах, в последнем случае нужно будет детально разобрать что находится в этих переменных. У меня на это ушло две минуты, это в проекте с 50000 строк кода.

Всё, что осталось найти — вызовы preg_replace с модификатором «e». Я, было, подумал как-то автоматизировать это дело, но просмотрел сколько у меня вхождений этой функции — оказалось около 200, быстрее просмотреть глазами. Мои старания были вознаграждены — я сделал несколько замен.

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

2 комментария

Пакетное скругление картинок v0.2

Новая версия пакетного скруглителя картинок. Из нового: короткие имена ключей (их видно после запуска), ключи для сохранения EXIF из оригинального файла (-​-save-exif), рекурсивного обхода папок (-​-recursive) и сохранения оригинальных имён файлов (-​-keep-name).

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

Микроцефал

Фото из самолёта (32.21КиБ)

Я люблю разглядывать инструкции для пассажиров из самолётов, там много смешного. Вот, например, ребёнок-микроцефал на коленях у отца, голени которого длиной с его туловище.

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

«Ты слишком правильный»

Я об этом уже говорил, ну да ладно, ещё скажу. Фраза «ты слишком правильный» по бессмысленности стоит где-то рядом с «всё с тобой понятно».

Человек произносящий «ты слишком правильный» как бы подразумевает, что оппонент живёт по правилам, тогда как он сам их нарушает, бунтует, чем выгодно отличается от.

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

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

Пакетное скругление картинок v0̶.̶2̶.̶1 0.2.2

Ребята, качайте новую версию, я сделал antialiasing для углов. Ну и пришлось же помучиться! Я немного заболел, пока сидел дома, сделал.

Основная идея такова — рисуется красный круг на чёрном фоне, на него накладывается фильтр Гаусса (тут меня ждала засада — в PHP 4.4.4, который у меня, нет функций imagefilter и imageconvolution, зато последняя, написанная на чистом PHP есть в интернeтах).

Далее, я считаю красную компоненту круга альфа-каналом для указанного цвета фона и накладываю по точкам в четыре угла картинки четыре квадрата изображения с красным кругом. Формула пересчёта rgba → rgb очень простая.

Получилось, на мой взгляд, очень неплохо, но скорость обработки, конечно, снизилась.

Добавлено позже: обновил до 0.2.2 — теперь вся работа с временными файлами делается внутри временной папки (путь до неё берётся из переменных окружения).

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

QR-код в ASCII

 ▄▄▄▄▄▄▄  ▄▄ ▄▄    ▄▄▄▄▄▄▄ 
 █ ▄▄▄ █    ▄▄▄▀ ▄ █ ▄▄▄ █ 
 █ ███ █ ▀▄▀▄▀ ▄ ▀ █ ███ █ 
 █▄▄▄▄▄█ █▀█▀▄▀█ ▄ █▄▄▄▄▄█ 
  ▄▄   ▄ ▀██▀██ ▀█ ▄▄ ▄    
 ▄▄█▀ █▄▀█▀▀▀▀▄▄█  ▀▀ █▄▀█ 
 ▄███ ▀▄ ▀  ▄ ▄█▄█ ▄█▀▀  ▄ 
 ▀ ▀▄▀▄▄▀▄▄▄█▄ ▀██▄▀█ ▄▄▀█ 
 ▀▀▄▄▄▀▄ ▀▀█ █▄▀▄███▄▄  ▄  
 ▄▄▄▄▄▄▄ ▀▄▄▀▄▄ ▀█ ▄ █▄▀ █ 
 █ ▄▄▄ █  ▄█▀█ ███▄▄▄█▄ ▄█ 
 █ ███ █ ▄██▀▀▄▀▀▀█  █▀▀█▄ 
 █▄▄▄▄▄█ ▀ ▀ ███ ██▀▀▀▄  ▄ 
 

Интересная штука — QR-коды в ASCII-графике. Можно внутри текста помещать, в обычном текстовом файле. Profit.

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

IE9 beta

Internet Explorer 9 beta (39.95КиБ)

Вышла первая бета-версия IE9. Идея разместить табы и адресную строку на одной прямой явно неудачная.

Сайты открываются через пень-колоду, явно хуже, чем в последней platform preview, мой сайт выглядит, как какашидзе, даром, что показывался одинаково во всех браузерах, включая самые старинные IE Mobile. Зато ACID3 — 95%, одно слово — синтетика.

Тьфу.

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

Белоярская АЭС

Есть такое направление — технотуризм, это посещение всевозможных закрытых учреждений и объектов. Мне давно хотелось посетить настоящую атомную электростанцию и вот, в прошлые выходные, мы с женой съездили на Белоярскую АЭС (находится в городе Заречный Свердловской области).

АЭС — закрытый объект, попасть туда простому смертному (а тем более посмотреть на реактор) крайне тяжело, сейчас очень удачный момент — «Росатом» заинтересован в пиаре отрасли и под эгидой популяризации блоггерам разрешается участвовать в довольно редких (несколько раз в год) экскурсиях.

Спасибо огромное агентству «Редкая марка» и  Бутяковой персонально за организованную экскурсию. Если кто-то захочет попасть на АЭС в ближайшем будущем, обращайтесь туда, можно написать лично.

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

Белоярская АЭС уникальна тем, что на её территории действует единственный в мире реактор на быстрых нейтронах БН-600 (мощность в настоящее время — 630 МВт), запущенный ещё в 1980 году — наследство СССР, инженерная школа страны была на таком уровне, что реактор этого типа (считается самым перспективным и современным) работает вот уже 30 лет без критических сбоев.

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

Дело не только в очень удачных инженерных решениях, но и в применённых материалах. Можно только гордиться промышленностью СССР, но вот такого же доверия к промышленности России у меня нет. Сейчас на территории АЭС возводится энергоблок БН-800 (на тех же быстрых нейтронах, как следует из названия), получится ли у нас ещё раз? Дирекцию настроена оптимистично, меня же гложут сомнения.

Что такое атомная электростанция изнутри? Моё воображение рисовало, довольно туманно, огромный реактор, вокруг куча обслуживающего персонала… На деле атомная электростанция — это трубы. Трубы, трубы, трубы, трубы и очень мало людей.

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

Самый совершенный атомный реактор, на настоящий момент, это обычная печка, энергия снимается через тепло. Кстати, в реакторе на быстрых нейтронах БАЭС в качестве теплоносителя используется жидкий натрий (это очень эффективно, но порождает массу технических трудностей, например, натрий при контакте с водяными парами взрывается).

Как и в моём воображении, на реальных АЭС всё управляется из больших комнат с кучей приборов, правда, люди не сидят сутками, напряжённо вглядываясь в их показания, а довольно расслабленно, даже вальяжно наблюдают за ситуацией. Такая расслабленность может моментально смениться слаженной активностью и умение мгновенно концентрироваться — главное качество, необходимое в этой работе.

Одна ошибка — курсы переквалификации, экзамены, вторая — прощай диспетчерская.

На фотографиях видно, что часть приборов современенная, часть, не иначе, осталась ещё с советских времён. Это закономерно — диспетчеры привыкают к определённым приборам и снимать показания с новых для них неоправданное неудобство. Тем не менее, приборы выходят из строя и обновления всё же производят, но дозированно.

Сам реакторный цех ещё более пустынный и огромный (десятки метров в любую сторону). Небольшая яркая бочка, окружённая «лепестками» — реактор. Не впечатляет, правда? Внутри реактора интересно до смерти, но в могилу мы не стремимся.

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

Общие впечатления — один раз съездить на такой объект интересно, в дальнейшем я бы посетил разве что музей в Обнинской АЭС (это первая гражданская АЭС в мире), не сомневаюсь, что остальные станции похожи между собой, на взгляд неспециалиста, как две капли воды.

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

PHP и UTF-8: четыре с половиной или некоторые функции

Я пока приостановил эпопею с UTF-8, так как сменились приоритеты, но обязательно ещё вернусь к этому вопросу. А пока, в паузах, потихоньку пишу для функций, для которых уже нет UTF-8-аналогов эти самые аналоги. Вот, например, очередной набор (это статические методы класса UTF, для краткости я убираю часть для работы с однобайтными строками):

public static function strcmp($str1, $str2) {
    // используется модуль intl
    return collator_compare(collator_create(''), $str1, $str2);
}

public static function strncmp($str1, $str2, $len) {
    return self::strcmp(self::substr($str1, 0, $len), self::substr($str2, 0, $len));
}

public static function strcasecmp($str1, $str2) {
    return self::strcmp(self::strtolower($str1), self::strtolower($str2));
}

static public function ctype_space($text) {
    return preg_match('/^\p{Zs}+$/us', $text);
}

Привожу в качестве иллюстрации того, что не всё так сложно как кажется. Стоит только сесть и начать.

Некоторые функции в PHP уже есть (mb_substr), какие-то требуют минимальных доделок (preg_replace), некоторые можно выразить через другие (trim через ltrim и rtrim), оставшиеся, конечно, придётся написать (strcmp, ltrim, addcslashes и так далее).

И, кстати, в ходе родился замечательный совет: если функцию трудно реализовать (например, str_ireplace), посмотрите, может проще переписать место, где она используется. У меня две функции были использованы ровно по одному разу.

Добавлено позднее: тщательно изучил вопрос сравнения Unicode, я выяснил, что производить сравнение так, как это сделал я нельзя, поэтому функцию strcmp я удалил, буду переписывать. Например, буква «ё» в Unicode стоит дальше «л», а должно быть наоборот.

Добавлено ещё позже: в PHP 5.3 появилось расширение Intl, основанное на известной библиотеке UCI, оказывается это расширение собирается и с PHP 5.2.4+, так что имеет смысл использовать именно его.

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

PHP и UTF-8: четыре и шесть десятых или ещё проблемные функции

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

Дальше
Для начала, у нас в проекте используются ord и chr — функции получения кода символа и получения символа по его коду. Понятно, что места, в которых эти функции используются жёстко завязаны на однобайтные кодировки. В некоторых случаях ничего страшного не случится, в других эти места придётся переписать. Я нашёл несколько случаев использования, переписать пришлось пока только одно место, ещё одно я отложил на будущее. Сами функции я переписал следующим образом:

static public function ord($character) {
        if (self::ON) {
            $codes = unpack('N', mb_convert_encoding($character, 'UCS-4BE', 'UTF-8'));
            return reset($codes);
        }

        return ord($character);
    }

    static public function chr($ascii) {
        if (self::ON) {
            return mb_convert_encoding(pack('N', $ascii), 'UTF-8', 'UCS-4BE');
        }

        return chr($ascii);
    }

Более простая, но тоже не самая очевидная функция — addcslashes. Она экранирует указанные символы слешем, представляя все символы с кодом большим 128 как восьмеричное число, например, буква «ф» (в кодировке cp1251), если указать её во втором параметре, будет представлена «\364». Вопрос в следующем — символы в UTF-8 могу занимать больше одного символа, как их заэкранировать?

Ответ в парной функции — stripcslashes. Если последовательно заэкранировать все байты UTF-8-последовательности символа, то stripcslashes вернёт этот символ в первоначальное состояние. Код реализации я приводить не буду, слишком длинный. Что о нём нужно знать — последовательность экранируется только в том случае, если встречается целиком.

Моя задача — эмулировать поведение функции в точности, в этой связи str_pad содержит две загадки: если задана строка дополнения из двух символов и более, то как аргумент дополняется слева и какая сторона дополняется первой, когда дополнять надо обе стороны?

bolk@dev:~/daproject$ php -r 'var_dump(str_pad("..", 3, "12", STR_PAD_BOTH));'
string(3) "..1"
bolk@dev:~/daproject$ php -r 'var_dump(str_pad("..", 5, "12", STR_PAD_LEFT));'
string(3) "121.."

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

Вообще, в PHP функций с тонкостями поведения масса. Функция wordwrap (по-умолчанию) преобразовывает длинную строку в абзац заданной ширины, разрывая строку так, чтобы не разорвать слова. А вот какими символами, по мнению этой функции, разделяются слова? Мои эксперименты показывают, что это пробел и «\n». Интересно, что «\r\n» эта функция не разрывает! Так же нужно не забыть про то, что пробелы, вообще говоря, сохраняются, заменяется на символ переноса только необходимый минимум.

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

«Community»

Только что, в шестнадцатой серии сериала «Сообщество», Абед Надир произнёс, напиваясь, «будем здоровы» по-русски (!).

Отличный сериал, кстати. Спасибо всем тем, кто советовал.

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

Хром → Опера

К сожалению, с «Хрома» (это браузер), к которому я уже привык и который обставил с любовью плагинами, мне придётся пересесть куда-то ещё. Особенно я привык к расширению, которое позволяет в моём «Андроиде» (это операционная система в телефоне) мгновенно открыть ту же страницу, что у меня в браузере и продолжить читать с того же места. Очень удобно.

Но. Недавняя неприятная история, когда в код «Хромиума» (это «Хром», но без «жучков») попал один из «шпионских» модулей, заставляет задуматься — что ещё, по небрежности или умышленно, могла забыть выключить корпорация «Гугл» в своём браузере? Тем более, что ещё на слуху история о том, что «Гугл» собирал данные WiFi-трафика при помощи автомобилей Street View.

Вообще-то, каких-то особых секретов персонально у меня нет, но они есть у «Яндекса» и мне очень не хочется, чтобы какие-то данные из интранета по моей вине попали в «Гугл». Так что, «Хром» и «Хромиум» я больше не использую. По крайней мере, в ближайшем будущем.

Перехожу обратно на «Оперу», других вариантов нет. «Сафари» под Windows — это какой-то чёрный юмор, FireFox похож на обросшую ракушками баржу, IE9 пока ещё сырой чуть более, чем полностью. Так что, при всё богатстве выбора, другой альтернативы нет.

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

Огромные пульты

Огромный пульт от телика (32.24КиБ)

В супермаркете сегодня увидел — универсальный пульт «Gal LM-X11». Интересен он размерами — 12,5×28×2,5 см (сантиметров!).

Как ни странно, но такого монстра я бы купил. Если бы у меня был телевизор. Во-первых, это забавно, во-вторых, он вряд ли потеряется. Даже если упадёт за диван, я это услышу (о, да! вес — почти 400 грамм). С миниатюрными пультами у меня в прошлом была вечная проблема «куда-же-я-его-дел».

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

Мёртвые японские души

Расследование министерства юстиции Японии не смогло обнаружить 234,354 тысячи человек старше 100 лет, которые по бумагам числятся живыми, в действительности их местопребывание, по обнародованным в пятницу данным, неизвестно.

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

И это министерство проверило только список столетних, если взять возраст младше, неизвестно сколько там таких «зомби». Мне интересно — если отфильтровать умерших, насколько снизится средняя продолжительность жизни в Японии?

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

НАМИ

Ездил сегодня ремонтировать обогреватель «AirComfort». Ничего обогреватель, простенький, потому и ломаться там особо нечему, спираль перегорела. Так бывает, повёз менять. Пишу себе заметку на будущее, если ещё сгорит. Замену осуществляет ровно одна организация — «Ремсервис», располагающаяся на территории НАМИ (это центральный научно-исследовательский автомобильный и автомоторный институт).

Схема территории «Ремсервис-СЕ»: 3 этаж, т. 1-46, показаны здания, подъездные пути и расположение объектов на плане.

Работает эта печальная организация с 10 до 17 по рабочим дням. С собой надо иметь паспорт и 1000 рублей. С паспортом идём в бюро пропусков, выписываем временный пропуск, по схеме, на третьем этаже находим «Ремсервис». Это организация из трёх человек — принимающей звонки женщины, мастера, который всё ремонтирует и дядьки, который ходит по комнате и разговаривает.

Замена спирали происходит всё в той же печальной комнате, где принимают клиентов и занимает полчаса. Институт находится на ул. Автомоторная, д. 2 (проезд автобусом №123 с метро Речной вокзал).

Одноэтажное розовое здание с красными дверями и желтыми предупреждающими знаками, окруженное зеленым забором и деревьями с осенними листьями на асфальте.
Осенний двор с жёлтыми листьями на асфальте, белым автомобилем слева, кирпичным зданием и зелёным забором справа под ясным небом.
Серебристый кроссовер припаркован под навесом у бежевого здания, на асфальте видны опавшие листья, на заднем плане — деревья и другие машины на парковке.
Синяя вывеска с белым текстом «ИНФОРМАЦИЯ» и логотипом РЖД над деревянной скамьей у стены здания с облупившейся краской.
Вид через открытую дверь в тамбур с турникетом, освещённый солнечным светом; на стеклянных дверях видны отражения соседнего здания и деревьев.
Чёрный металлический забор с колючей проволокой и табличкой «А 123 будни 20:29 21:06 22:38 23:08 0:14 вых. дни 21:41 22:41 23:41 0:46» на фоне зелёной травы и деревьев.

Вообще НАМИ прекрасный образец «усадьбы разорившегося помещика» — видно, что денег когда-то было очень и очень много, а сейчас организация дышит на ладан.

Дополнение: нагревательный элемент поставили хуже, чем тот что бы. Новый светит куда больше старого, соответственно, греет меньше.

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

PHP и UTF-8: пятый этап

Итак, пятый этап. Точнее на этап, он, пожалуй, не тянет, но после этапов 4,5 и 4,6 мне хочется целых чисел.

Итак, пятый этап
Я заметил, что у меня, в моих аналогах строковых функций, умеющих работать с UTF-8, очень много последовательных проходов по строке. После Пайтона, с его итераторами и генераторами, у меня руки чесались оптимизировать это место.

В самом деле, если простое взятие символа по индексу в строке с фиксированным размером символа имеет сложность O(1), то в UTF-8 строке эта сложность возрастает до O(n). Соответственно, когда я прохожу циклом по строке с фиксированным размером символа эта сложность — O(n), а текущие циклы в моих функциях проходят строку UTF-8 за O(n²). Особенно это будет заметно в функции rtrim.

Я написал небольшие тесты. Замерял три прохода по тексту объёмом 187,5КБ. Проход по строке с однобайтной кодировкой — 0,2 секунды, по строке UTF-8 — 160 секунд. Забегая вперёд, скажу, что проход той же строки (UTF-8) итератором занимает 4,3 секунды, результат хуже, чем у встроенных функций, но в 37 раз лучше (на этих данных), чем проход прежним алгоритмом.

Вообще-то в PHP5 появилось очень много вещей, которые никто не использует. Свежие книжки ещё не появились, а документацию никто ни читает. Поэтому один из моих любимых вопросов на собеседовании по PHP — «что бы вы сделали, если бы вам в PHP понадобился двусвязанный список» (что-что, использовал бы SplDoublyLinkedList), впрочем если претендент не отвечает, в минус ему это не идёт, зато правильный ответ идёт в плюс.

Так вот, одна из вещей, которой мало кто пользуется — возможность создать в PHP свой итератор. У меня тут не руководство по итераторам, поэтому я ограничусь лишь кодом и примером использования. Вообще-то мне в коде понадобятся два итератора (второй будет итерировать строку с конца), но они очень похожи, поэтому я приведу только один.

Основная идея — если мы двигаемся по строке последовательно, где-то сохранять текущую позицию, тогда к следующему символу нам перейти будет проще (сложность — O(n)).

UTF-8 достаточно хорошо устроен — первый байт символа всегда можно отличить по виду от его хвоста. У «хвоста» старшие биты всегда выглядят как «10», вот и весь критерий. Так что мы всегда можем сказать где начался новый символ — там где старшие биты байта не совпали с «10».

class UtfString implements Iterator
    {
        protected $str;   // строка
        protected $idx;   // бинарный индекс строки
        protected $pos;   // UTF-8-индекс
        protected $len;   // бинарная длина
        protected $next;  // бинарный указатель на следующий

        protected $idx_start; // бинарный индекс откуда стартуем
        protected $pos_start; // UTF-8-индекс откуда стартуем

        // проверка — байт не является началом символа
        protected function isNotBegin($ch)
        {
            return (ord($ch) & 192) == 128;
        }

        public function __construct($str, $pos_start = 0)
        {
            $this->str = $str;
            $this->pos = $this->pos_start = $pos_start;
            $this->len = strlen($str);
            $this->next = null;

            if ($pos_start) {
                for ($i = 0; $i<$pos_start; $i++) {
                    $this->idx_start++; 

                    while ($this->isNotBegin($this->str[$this->idx_start])) {
                        $this->idx_start++;
                    }
                }
                $this->idx = $this->idx_start;

            } else {
                $this->idx = $this->idx_start = 0;
            }
        }

        public function current()
        {
            $idx = $this->idx;
            $out = $this->str[$idx++];

            while ($this->isNotBegin($ch = $this->str[$idx])) {
                $out .= $ch; // быстрее, чем массив!
                $idx++;
            }

            $this->next = $idx;

            return $out;
        }

        public function key()
        {
            return $this->pos;
        }

        public function next()
        {
            $this->pos++;

            if ($this->next) {
                $this->idx  = $this->next;
                $this->next = null;
            } else {
                for ($this->idx++; $this->isNotBegin($this->str[$this->idx]); $this->idx++);
            }
        }

        public function rewind()
        {
            $this->idx = $this->idx_start;
            $this->pos = $this->pos_start;
        }

        public function valid()
        {
            return $this->idx < $this->len;
        }

        public function __toString()
        {
            return $this->str;
        }
    }

Использовать итератор достаточно просто:

$str = str_repeat('Привет', 64000);

    foreach (new UtfString($str) as $position => $char) {
        // …
    }

Так как в итерации сначала вызывается «current», а потом «next» мне показалось странным терять информацию о положении следующего символа, которую мы в «current» уже знаем, так что я передаю её в «next» через защищённое свойство.

Список функций (из моего списка используемых функций), которые я перепишу с итератором (или скорость которых увеличится): trim, ltrim, rtrim, wordwrap, strcspn и strspn, а так же некоторые места в коде проекта.

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

Гостиница «Которосль» в Ярославле

Номер категории «Люкс» в гостинице «Которосль» — это, ребята, сидячая ванна, механические замки, шумный холодильник «Смоленск» (этакий отбойный молоток в ночи), отсутствие халата, зубной щётки, мусорки, сейфа и интернета. Правда мебель чистенькая и новая, хотя и тошнотворная, но я не понимаю почему это называется «Люкс». Чего тут «люксового»?

Добавлено на следующий день: вот так выглядит спальня в номере, обратите внимание на оригинальную конструкцию лампы. Нет, это не реализация идей Тесла по беспроводной передачи электричества, это просто провод обрезан.

Номер «Люкс» (22.98КиБ)

Кстати, на конференции ADD, где я сейчас нахожусь, Windows 7 отказалась подключаться к интернету, пришлось на телефоне («Андроид» подключился без проблема и сразу) посмотреть выданный IP-адрес и прописать в Windows всё статически. Что это было я не знаю.

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

PHP4 без XML-парсера

Мне тут по работе пришлось решить странную, по нынешним временам задачу — принимать XML в программе на PHP4.3+, где никакого парсера XML может не оказаться. Таковы реалии продукта и среды, где он будет использоваться (это код внешнего проекта). Поэтому мне пришлось написать небольшой парсер, который, правда, расcчитывает, что XML будет well-formed (я его получаю из доверенного источника).

Дальше
Я далёк сейчас от shared-хостингов, но знаю, что до сих пор встречаются самые странные конфигурации, возможно кому-то пригодится, потому выкладываю.

<?
// Very Simple XML SAX Parser by Evgeny Stepanischev
// /
class VerySimple_XMLParser
{
    var $saxCallback;

    function VerySimple_XMLParser($saxCallback)
    {
        $this->saxCallback = $saxCallback;
    }

    function parse($xml)
    {
        if (preg_match_all('#<![CDATA[.*?]]>|</?[^>]*>|[^<]+#us', $xml, $matches)) {
            foreach ($matches[0] as $match) {
                $this->parseThis($match);
            }
        }
    }

    function parseThis($match)
    {
        $calls = array(
            '<!--'      => 'COMMENT',
            '<?'        => 'PI',
            '<![CDATA[' => 'CDATA',
            '<!'        => 'DTD',
            '</'        => 'TAGCLOSE',
            '<'         => 'TAGOPEN',
        );

        foreach ($calls as $need => $type) {
            if (strpos($match, $need) === 0) {
                return $this->parseByType($type, $match);
            }
        }

        return $this->parseByType('OTHER', $match);
    }

    function parseByType($type, $text)
    {
        if ($type == 'COMMENT') {
            return $this->addChunk($type, substr($text, 4, -3));
        }

        if ($type == 'CDATA') {
            return $this->addChunk('TEXT', substr($text, 9, -3));
        }

        if ($type == 'OTHER') {
            return $this->addChunk('TEXT', $text);
        }

        if ($type == 'TAGCLOSE') {
            $end = strcspn($text, ' >');

            return $this->addChunk('TAGCLOSE', substr($text, 2, $end - 2));
        }

        if ($type == 'TAGOPEN') {
            $this->addChunk('TAGOPEN', $this->parseTag($text));

            if (substr($text, -2) == '/>') {
                $end = strcspn($text, ' >');

                return $this->addChunk('TAGCLOSE', substr($text, 1, $end - 2));
            }
        }

        return '';
    }

    function parseTag($text)
    {
        $end = strcspn($text, ' />');
        $name = substr($text, 1, $end - 1);

        return array($name, $this->parseTagAttributes(substr($text, $end)));
    }

    function parseTagAttributes($text)
    {
        $nameStart = '(?:[A-Za-z_:]|[^\x00-\x7F])';
        $nameChar  = '(?:[A-Za-z0-9_:\.-]|[^\x00-\x7F])';
        $attrValue = '(?:\'[^\']*\'|"[^"]*")';

        if (preg_match_all("/{$nameStart}{$nameChar}*=$attrValue/us", $text, $matches)) {
            return $this->parseAttributes($matches[0]);
        }

        return array();
    }

    function parseAttributes($rawAttributes)
    {
        $attributes = array();

        foreach ($rawAttributes as $rawAttr) {
            list($name, $value) = explode('=', $rawAttr, 2);
            if ($value[0] == '"' || $value[0] == "'") {
                $value = substr($value, 1, -1);
            }

            $attributes[$name] = $value;
        }

        return $attributes;
    }

    function addChunk($type, $arg)
    {
        call_user_func($this->saxCallback, $type, $arg);
    }
}

// Пример использования

$xml =<<<XML
<!-- комментарий -->
<?xml version="1.0"?>
<root attribute="a.a" v="a">
    <v><![CDATA[data]]></v>
    <v>data</v>
    <v/>
</root>
XML;

// Понятно, что можно написать класс и передать в качестве
// callback в парсер метод объекта, но я для простоты, как пример,
// написал очень простую функцию
function VerySimple_XMLParser_Collector()
{
    static $tree = array();

    if (func_num_args() == 2) {
        $tree[] = func_get_args();
    } else {
        return $tree;
    }
}

$sax = &new VerySimple_XMLParser('VerySimple_XMLParser_Collector');
$sax->parse($xml);

echo '<plaintext>';
var_dump(VerySimple_XMLParser_Collector());

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

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

IE9 и его скорость, по сравнению с другими браузерами

Сижу сейчас на семинаре Михаила Черномордикова из Microsoft «HTML5, CSS3 и новый Internet Explorer 9». На экране — известный тест Flying Images, который Михаил демонстрирует в IE9 бета1 и FF 4 (неизвестной) бете.

У IE9, судя по всему сильное преимущество. Я как-то засомневался, помню на «Хабре» был материал, из которого следовало прямо обратное — как только в FF4 появилось аппаратное ускорение, IE9 остался далеко позади.

Скачал FF 4 beta7pre, включил в конфигурации всё, что относится к аппаратному ускорению, потестировал. В общем, Microsoft… вводит нас в заблуждение (как и статья на «Хабре»). FireFox явно впереди, хоть и незначительно.

33 комментария

Пасхальное яйцо в Lixil CMS

Скриншот веб-страницы teletrade.ru с адресной строкой http://old.t... и чёрно-белым фото мужчины в спортивной форме, держащего ракетку.
Пасхальное яйцо

Когда-то в фирме «Lixil», совладельцем которой я когда-то был (сейчас не существует), мы делали «Lixil CMS». Достаточно неплохая штука получалась, кстати. Но речь не об этом. В одну из версий этой CMS я встроил «пасхальное яйцо» — свою фотографию. Причём, фотография собиралась достаточно запутанным куском кода на PHP (если найду, покажу), а бинарная информация была раскидана по всем библиотеками в виде комментариев.

Сегодня я случайно обнаружил один из сайтов на той CMS и оказалось, что пасхальное яйцо до сих пор работает, его можно увидеть, если послать RPC-запрос (эти запросы использовались в AJAX-интерфейсе) специального вида: http://old.teletrade.ru/?LRPC=90&args={}

Картинку можно увидеть только в IE, так как не выдаётся HTTP-заголовок картинки, ну или в других браузерах можно использовать dataURI:

data:text/html,<img src="//old.teletrade.ru/?LRPC=90&args={}">

Вставлял я этот кусок кода ради теста — найдут ли программисты компании его в коде и смогут ли понять что он делает.

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

Bitrix WTF

Я сейчас в Ярославле слушаю доклад по клиентским сертификатам.

Рядом со мной сидит Коля Мациевский, он один из первых в России начал комплексно исследовать вопрос оптимизации загрузки сайтов и владеет сейчас первой в России компанией «WeBo», которая этим занимается. Коле скоро выступать, а пока он ковыряет исходники своего продукта, а точнее плагина, который встраивает упомянутый продукт в CMS «Bitrix».

Причина в том, что внутренний антивирус «Bitrix» почему-то вырезает JavaScript, который подключается плагином. Проблема оказалась в следующем коде:

$ll = strlen(bin2hex($this->body))/2;

if($ll > 500)
{
    $val = 9;
    $r += $val;
    $this->resultrules['rulescriptlenghts_sl'] = $val;
    return $r;//только одно правило из блока
}

Вот эта странная во всех отношениях (почему не просто strlen?) проверка считает вирусным любой JS-код, строка которого занимает больше, чем 500 байт.

4 комментария

3G в Ярославле

Интернет в гостинице (42.48КиБ)

Ловил два дня 3G в Ярославле. Кстати, да. В номере «Люкс» есть радиоточка!

2 комментария

House M.D.

Сериал «Сообщество» мы посмотрели ещё на прошлой неделе, начали «Доктора Хауса». В шестой серии первого сезона, в отражении стеклянной двери смутно видно съёмочную группу.

2 комментария

Тонкости heredoc в PHP

В PHP, как и в некоторых других языках (Bash, Perl) есть конструкция heredoc — это способ задать многострочную строку. Посмотрите как удобно, не нужно экранировать кавычки:

// это PHP
$str = <<<STR
Привет, я строка "в кавычках",
'в апострофах'.
STR;

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

Сегодня, в очередной раз раздражаясь по этому поводу, я решил попробовать использовать неразрывные пробелы (Alt Right+Space в раскладке Бирмана), получилось, работает даже в PHP 4.4.4. Подходит, конечно, для исходников в кодировке UTF-8. Надеюсь, вы в другой кодировке их уже и не пишете.

Правда, строки внутри, конечно, менять нельзя, но эта конструкция часто используется вместе с HTML, а ему отступы неинтересны.

$str = <<<    HTML
    <table>
        <tr class="row"><td>'Привет'</td></tr>
    </table>
    HTML;

Только не забудьте, что отступы надо делать и в начальной конструкции, и в конечной.

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

Ярославль

Ярославль — город, входящий в Золотое кольцо России, наверняка, один из красивейших городов России, жаль у меня не было времени в этом убедиться. Надо будет приехать туда летом.

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

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

Я помню празднование наглого тысячелетия в Казани (в 2005-м), кругом новый асфальт, урны, покрашенные заборы и прочее,… с Ярославля либо не пытались сдуть пыль, либо было ещё хуже.

Синий флаг с гербом, изображающим чёрного медведя на белом фоне, развевается на столбе на фоне серого неба и голых ветвей деревьев.
Баннер с гербом Ярославля (медведь с копьем) и надписью «Ярославль» на фасаде здания, перед ним припаркованы автомобили.
Граффити с гитарой и надписью «1000 мелодий» на стене здания, вид из-за припаркованного автомобиля в дождливый день.
Вход в ателье с вывеской «РЕМОНТ ПОШИВ» и списком услуг: одежда, изделия из кожи, меха, ткани. Слева — табличка с режимом работы.
Флаг с гербом и надписью «Архангельск» на фоне серого неба, прикрепленный к столбу с проводами.
Белая входная дверь с синим логотипом «Аромакафе» в виде трёх кофейных зёрен и надписью, установленная в розовой стене.

Я люблю думать, гуляя по улицам какого-либо города — что если мне завязать глаза и повозить 5-6 часов, а потом выпустить, отличил бы я — вывезли меня из Москвы или я ещё в её пределах? Мне показалось, что, выпусти меня в Ярославле, я бы понял, что я не в Москве. Но я так и не понял, на чём это ощущение основано.

Кажется, в Ярославле всё те же не прополотые корни прошлого, что и в Москве…

Фасад здания с надписью «ЮНЫЙ ТЕХНИК» и абстрактной мозаикой из геометрических фигур и слов «САНТЕХНИКА», «МАТРИЦА», «ОБЩЕСТВЕННОЕ
На фасаде здания вывеска «IDEA ГИПЕРМАРКЕТ МЕБЕЛИ» с надписью «СКОРО ОТКРЫТИЕ», адрес: Полушкина роща, д. 9, телефон: 67-17-67, логотип «ТАНДЕМ».
Белая статуя мальчика на колонне в саду среди хвойных деревьев с желтой листвой и цветущих кустарников.
Бронзовый памятник сидящему мужчине на высоком каменном постаменте с надписью «В.И. Ленин» на фоне здания с арочными окнами и деревьев.

То же народное творчество на стенах…

Мурал на кирпичной стене: реалистичное изображение головы собаки в окружении стилизованных цветов и листьев, частично скрыто деревьями, на земле — опавшие листья и лужи.
Заброшенное здание с разбитыми окнами и кондиционерами, вдоль которого тянется бетонная стена, покрытая граффити, на фоне зелёной травы.

Те же однообразные и одинаковые по всей стране пятиэтажки и старые дворики…

Детская площадка с качелями и качалкой-балансином, огороженная рядом разноцветных покрышек, на фоне жилого дома и деревьев.
Мокрая городская улица с движущимися автомобилями, пешеходным переходом и жилыми домами с магазинами на первых этажах под дождём.
Два человека идут по осеннему парку мимо жёлтого многоэтажного дома с магазинами на первом этаже; на переднем плане — деревья и упавшие листья, на дороге — чёрный внедорожник.
Черный автомобиль припаркован на асфальтированной дороге между двумя желтыми жилыми домами, окружёнными высокими деревьями с желто-зелёной листвой в солнечный день.

Те же памятники старины…

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

То же самое разгильдяйство везде — даже на кладбище…

Надгробие с портретом и цветами на фоне других памятников и ржавой решётки среди осенних листьев.
Погребальный памятник с табличкой «Ефимов Владимир Прокофьевич 28.04.1943–2017», окружённый зелёной растительностью и чёрной металлической оградой.

Конечно, может быть я узнал о том, что не в Москве по многочисленным упоминаниям названия города…

Фасад здания Дома культуры железнодорожников узла Ярославль-Главный с красной лентой «Россия живет дорогами» и логотипом РЖД
Жёлтое здание вокзала с часами на башне и надписью «Казанский вокзал», перед ним — автомобили, автобусы и пешеходы на улице под облачным небом.

Или по реке, разве река Москва течёт так же несуетно?

Длинный металлический мост через реку под пасмурным небом, с набережной и перилами на переднем плане.
Вид на реку с набережной: вдали белое теплоходное судно, берег с осенними деревьями и зданиями, на воде два буя, на переднем плане — чёрное ограждение и фонарь.
Металлический железнодорожный мост через реку на фоне осеннего берега и пасмурного неба.
Белый теплоход стоит у берега реки на фоне осенних деревьев и моста; в воде видны три буя под пасмурным небом.
Вид на набережную с мокрой асфальтированной дорожкой, зелёными газонами, фонарями и перилами вдоль реки; на воде — буйки и теплоход, на заднем плане — мост и осенние деревья.

Может отличаются люди? Или они везде одинаковые?

Прогулка по осенней аллее у белого здания с колоннами: люди идут по мокрому тротуару, деревья в жёлто-зелёной листве, на заднем плане — припаркованные машины.
Большой рекламный щит с надписью «МАРИЯ» и логотипом ASUS на фоне осенних деревьев и мокрой дороги с автомобилями и светофором.
Жёлтое здание с вывеской «РОСТЭК» и информационным стендом, рядом — серебристый автомобиль, пешеход и деревья на фоне другого здания под облачным небом.
Четверо людей идут по тротуару вдоль здания с розово-белым фасадом и вывеской «Книжный магазин», рядом — ряд деревьев и припаркованные автомобили.
Осенняя улица с деревьями, покрытыми жёлтыми листьями, и металлической рамой на тротуаре; справа — здание с вывеской «Интер Кафе», слева — парковка с автомобилями.

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

Он был не рад мне, а я был не рад промокшим ногам, сырости и холоду. В следующий раз, когда приеду, позвоню заранее, может мы ещё понравимся друг другу.

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

PHP4 без XML-парсера, плюс XPath

Я недавно написал простенький парсер XML для PHP 4.3+, для случаев, когда никакого встроенного парсера XML на хостинге не оказалось.

А вчера мне подумалось, что парсить XML в дерево это, конечно, замечательно, но пользоваться этим всё равно неудобно. Было бы неплохо дать возможность получать произвольные узлы так же просто, как это делается при помощи XPath.

Весь XPath я, конечно, реализовывать не буду, да мне столько и не надо. В общем, написал класс, который используется в паре VerySimple_XMLParser. Реализованы получение потомков тега, «*», «node()» и «text()». Это всё, но мне хватит.

// Simple XPath for VerySimple_XMLParser
// /
class VerySimple_XMLParser_Tree
{
    var $path;
    var $counter;
    var $xpath;
    
    function VerySimple_XMLParser_Tree()
    {
        $this->path = array('/');
        $this->counter = 0;
        $this->xpath = array();
    }


    function collect($type, $args)
    {
        $path = implode($this->path);

        switch ($type) {
            case 'TEXT':
                $this->xpath[$path . $this->counter . '/text()/'] = $args;
                $this->counter++;
                break;

            case 'TAGOPEN':
                list($name, $attributes) = $args;

                $tag = $this->counter . '/' . $name . '/';

                $this->xpath[$path . $tag] = $attributes;
                $this->counter = 0;

                $this->path[] = $tag;
                break;

            case 'TAGCLOSE':
                $this->counter = 1 + (int) array_pop($this->path);
                break;
        }
    }

    function halfxpath($path)
    {
        $keys = array_keys($this->xpath);
        $trans = array(
            '\/'       => '\/\d+\/',
            '\*'       => '[^\/()]+',
            'node\(\)' => '[^\/]+',
        );

        $filter   = strtr(preg_quote($path, '/'), $trans);
        $filtered = preg_grep("/^$filter\/$/su", $keys);

        $nodes = array();

        foreach ($filtered as $key) {
            $nodes[$key] = $this->xpath[$key];
        }

        return $nodes;
    }
}

// а вот так это можно использовать:
$xml =<<<XML
<root>
    <a>
        <b><c>Xa!</c></b>
        <b>Hello</b>
    </a>
    <a name="value"/>
</root>
XML;

$tree = &new VerySimple_XMLParser_Tree();

$sax = &new VerySimple_XMLParser(array(&$tree, 'collect'));
$sax->parse($xml);

echo '<plaintext>';
var_dump($tree->halfxpath('/*/a/b/text()'));
var_dump($tree->halfxpath('/*/a/b/node()'));
var_dump($tree->halfxpath('/*/a/b/*'));
var_dump($tree->halfxpath('/root/a'));

Метод для запросов я назвал «halfxpath», намекая, что это не настоящий XPath, а лишь неполноценная эмуляция.

Основная идея, как видно из кода — не парсить XML в дерево, а помещать каждый элемент по специального вида пути, который потом будет проверен на совпадение по регулярному выражению, приготовленному из запроса.

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

Собираемся менять фотоаппарат

Мыльница как-то всё меньше устраивает (у нас Sony Cyber-shot DSC-N1). Чуть облака на небе и всё, одни шумы, ничего не видно, снимаешь в помещении — всё, уводит в зелёный.

Последние несколько лет я себе на день рождения дарю сотовый телефон, а в этом году, как назло Nexus One меня всем устраивает, даже удивительно. Придётся поменять фотоаппарат.

Пока думаем брать Canon EOS 550D. Подумаем ещё немного и возьмём.

24 комментария

«Андроид» 2.2.1

У меня вчера «Андроид» на телефоне обновился, выкачав новую прошивку через EDGE (бедняга, попросил бы, я б WiFi включил). В интернетах пишут, что залатали пару дыр, а из того, что я заметил — появился твёрдый знак на клавиатуре.

Дополнение: поправили ошибку с именованием дней недели.

4 комментария

SQL-инъекция в штрих-коде?

*\'; DROP TABLE products; --* (2.32КиБ)

А вот ещё идея возникла. Распечатываете этот штрих-код на стикеры, и незаметно клеите их рядом с ценниками в супермаркетах конкурента.

Когда в конце рабочего дня будут производить учет товаров какой-нибудь доброход-товаровед обязательно посветит на него штрих-код сканером. :)

А там написано *’; DROP TABLE products; —*

P.S. Я и сам принимал участие в разработках программ складского учета с применением сканеров штрихкода. И знаю — никто там, естественно, входные данные не проверяет, чаще всего шпарят прям напрямую запросы с динамическими параметрами в SQL-базу. А теперь вот задумался, прогресс-то не стоит на месте… :)

Забавно, неужели правда сработает?

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

Optkit: можно ли автоматизировать мою графику?

Скриншот PowerShell с выводом таблицы сравнения размеров файлов (0.png, 1.jpg, 2.gif, 3.gif) и заметкой о конвертации GIF в PNG.

Выкладываю optkit — программу (написана на PowerShell v1, это значит — под Windows), которая определяет можно ли оптимизировать графику в форматах PNG, GIF, JPEG в указанной папке. При запуске нужно указать папку.

Примерный вывод есть на скриншоте. Выводится сколько удалось сэкономить, для GIF выводится сколько они будут занимать в PNG (если это даст экономию).

Программа использует ряд других программ, которые должны лежать в папке utils, это:

  • SuperGIF 1.5 (должен быть «купленным»)
  • GIFLite (можно поискать FTP-поиском), утилита не используется для 64-битных ОС
  • jpegtran
  • optipng
  • pngout (нужна command line версия)
    Моя программа не использует никаких продвинутых техник (например, не пытается избавиться от прозрачных, но не одноцветных участков), может ещё руки дойдут и сделаю.

Дополнено: Харисов подсказывается, что на Bash есть инструмент интереснее — imgcomp и писался явно не пару вечеров, как мой. Кстати, сравнивая две программы (на Bash и PowerShell) можно сделать интересные выводы о читаемости этих языков и их мощности.

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

← сюда туда →