программирование
Недокументированный Memcached-2
У модуля Мемкешед для ПХП удивительно плохо с документацией, я иногда заглядываю в его исходники, с целью почитать. Недавно наткнулся на очередную опцию, описания которой в документации нет (причём в документации аналогичного модуля для Пайтона она есть).
Оказываются UDP не единственный способ ускорить работу с Мемкешед, если вам скорость важнее результата операции. Способ хорош тем, что используется протокол TCP (т. е. с гарантированной доставкой), но мы не ждём — нам главное, что
Посмотрите на пример и обратите внимание на опцию OPT_NOREPLY.
$m = new Memcached;
$m->addServer(0, 11211);
$val = str_repeat(file_get_contents('/etc/passwd'), 200);
$m->set('key', $val);
$m->setOption(Memcached::OPT_NOREPLY, true);
$m->setOption(Memcached::OPT_COMPRESSION, false);
$time = microtime(true);
$m->append('key', $val);
echo number_format(microtime(true) - $time, 4, ',', ''), "\n";Кстати, такое большое (как в примере) значение мне потом обратно прочитать не удалось, видимо превысил
Интересно, что можно делать несколько операций append подряд, очерёдность их сохраняется, корректность прочитанного — тоже. Вот только непонятно не может ли чтение получить данные между двумя append, скорее всего — может, но этот способ как раз для случаев, когда на первом месте скорость записи к кеш.
Преобразование tnsnames.ora в JSON
По работе сделал разбор файла tnsnames.ora — файла оракловой конфигурации на ПХП в массив:
function Ora2Array($str)
{
// убираю комментарии
$str = preg_replace('/^\s*#.*$/m', '', $str);
// преобразование скаляров
$str = preg_replace('/\((\w+)\s*=\s*([^()]+)\)/m', '"$1": "$2", ', $str);
// преобразование ассоциативных массивов
do {
$str = preg_replace('/\((\w+)\s*=\s*([^()]+?)[\s,]*\)/sS', '"$1": { $2 },', $str, -1, $count);
} while ($count);
// преобразование верхнеуровневых значений
$str = preg_replace('/(\w+)\s*=\s*([^()]+?)[\s,]*(?:(?=\w+\s*=)|\s*$)/s', '"$1": { $2 },', $str);
return json_decode('{' . trim($str, ' ,') . '}', true);
}Не разбираются значения в кавычках — такого в наших конфигурациях не встречается, но добавляется несложно.
Memcached и UDP
Когда я написал про поддержку модулем Мемкешед для ПХП протокола UDP, я ещё не знал многих подробностей. Например, я думал, что ребятам из Мемкешед
Оказывается ничего подобного — UDP нужен только тогда, когда вас устраивает негарантированная доставка, т. е. вы швырнули пакет, а дойдёт ли он до кеша — неважно, при чтении это будет считаться промахом кеша.
В интернете есть статья, которая кратко раскрывает что это даёт, в ней же приводится патч «multi UDP», который Мемкешеду уже не нужен — я смотрел в код, всё там нормально давно.
Кстати, обнаружились две подробности в поддержке UDP модулем Мемкешед ПХП.
Работоспособный тест выглядит так:
$m = new Memcached('SomeID');
$m->addServer('127.0.0.1', 11211, 1);
$m_udp = new Memcached();
$m_udp->setOption(Memcached::OPT_USE_UDP, true);
$m_udp->addServer('127.0.0.1', 11211, 1);
var_dump($m_udp->set('foo', "test string"));
sleep (1);
var_dump($m->get('foo'));99 бутылок: Batsh, VIM script
Потихоньку возобновил наполнение раздела «99 бутылок пива на стене» — тут я пишу американскую считалочку про пиво на различных языках программирования. Добавились языки VIM script и Batsh. Первый используется в консольном редакторе vim для написания внутренних скриптов, второй — для кроссплатформенного написания
Batsh мне вообще понравился своей идеей — простой
50. Batsh — чрезвычайно лёгкий в освоении язык
// Песня о пиве на языке Batsh Степанищева Евгения
function bottles(bottle){
if (bottle == 0) {
return "no bottles";
}
if (bottle == 1) {
return "1 bottle";
}
return bottle ++ " bottles";
}
i = 99;
while (i > 0) {
b = bottles(i) ++ " of beer";
println(b, "on the wall,", b ++ ".");
i = i - 1;
println("Take one down and pass it around,", bottles(i), "of beer on the wall.\n");
};
println("No more bottles of beer on the wall, no more bottles of beer.");
println("Go to the store and buy some more, 99 bottles of beer on the wall.");49. VIM Script — встроенный скриптовый язык программирования консольного редактора VIM. Синтаксис довольно обычен, учится легко. Мощный для своего класса — обилие встроенных функций, довольно согласованный синтаксис, различные типы массивов. Есть интересные особенности, например, возможность явного переопределения функций, префиксы переменных для явного определения зоны видимости (например, префикс «a:» у меня в функции — это аргументы) или функции, которые могут принимать
" написал Евгений Степанищев bolknote.ru"
:function Bottles(bottle)
: return get(["no bottles", "1 bottle"], a:bottle, a:bottle . " bottles")
:endfunction
:for beer in range(99, 1, -1)
: let b = Bottles(beer) . " of beer"
: echo b "on the wall," b . "."
: echo "Take one down and pass it around, " . Bottles(beer-1) . " of beer on the wall.\n"
:endfor
:echo "No more bottles of beer on the wall, no more bottles of beer."
:echo "Go to the store and buy some more, 99 bottles of beer on the wall."is_numeric
Функция is_numeric одна из традиционно неверно понимаемых программистами на ПХП. Её, обычно, понимаю как «посмотреть содержится ли в переменной
Беда в том, что понятия числа у ПХП и человека различаются. Посмотрите, все эти вызовы рассматриваемой функции вернут «true»:
var_dump(is_numeric("123e123")); // true — десятичное число в экспоненциальной записи
var_dump(is_numeric("0123")); // true — восьмеричное число
var_dump(is_numeric("0XDeadBeef")); // true — шестнадцатеричное число
var_dump(is_numeric(NAN)); // true — специальное численное значение «не число»Особенно не везёт восьмеричным числам, которые во многих языках (и в ПХП) тоже начинаются с нуля: формально выглядят как обычное десятичное (только начинается с нуля), проходят все проверки, а после преобразования теряют ведущий ноль и это может быть важно.
С незапамятных времён существует хак, который позволяет убедиться, что перед нами именно десятичное число, пусть и в строковой форме:
function is_number($var) { return (string) (int) $var === (string) $var; }Принцип очень простой — переменная преобразуется в целый тип (можно преобразовывать и в тип с плавающей точкой, если это нужно), потом берётся строковое представление получившегося числа. На этом этапе восьмеричные, шестнадцатеричные и прочие виды записи чисел будут преобразованы в целочисленный тип, строковое представление которого — десятичное число. Справа же пришедшее остаётся в первозданном виде.
Далее мы сравниваем левую и правую часть и, если они совпадают, значит к нам пришло именно десятичное число, так как никаких преобразований с ним не произошло.
Добавлено позднее: в документации к функции написано, что она так же понимает двоичную форму записи введёную недавно (число должно начинаться с «0b»), к сожалению, это неправда — в коде поддержки нет, баг заведён и в нём написано, что это проблема документации, а не функции.
Интересно ещё, что is_numeric пропускает любое количество пробельных символов слева, но плохо относится к ним справа:
var_dump(is_numeric("\f\f\t0xBadBabe")); // true
var_dump(is_numeric("1\t")); //falseНедокументированные возможности модулья Memcached (для PHP)
Читая на досуге исходники модуля Memcached для ПХП, наткнулся на неожиданное: оказывается этот модуль поддерживает соединение по протоколу UDP, причём документация молчит об этом.
Делается это вот так:
$udp = new Memcached();
$udp->setOption(Memcached::OPT_USE_UDP, true);
$udp->addServer($host, $port);В частности, это позволит нам перейти на
Другая вещь, которая никак не раскрывается документацией — пользовательские флаги (UDF — user defined flag). К каждому значению можно прикрепить один или несколько флагов, которые при получении значения будут складываться в заданную переменную:
const FLAG = 1;
const ANOTHER_FLAG = 2;
$m->set($key, 'value', 0, FLAG | ANOTHER_FLAG); // установили флаг
$m->get($key, null, null, $flags); // флаги будут лежать в переменной $flagsУ каждой функции установки и получения значений (а их несколько, этих функций) в конце есть необязательный параметр для работы с флагами. Так что во всех вариантах использования значения можно
Позднее дополнение: похоже memcached больше не нуждается в multiport UDP patch, он и без него теперь с UDP работает нормально.
Короткий способ определить есть ли поддержка dataURI
Придумал короткий способ определить поддержку data URI в браузере:
<script src="data:text/javascript,self.dataURI=1"></script>Потом просто проверяете у window свойство dataURI, если оно есть, то есть и поддержка. Способ не работает в IE8 и IE9 — тот и другой не умеют загружать Джаваскрипт этим образом. Собственно, меня это не волнует — мне как раз и нужно определить, что Джаваскрипт так работает, но если вам нужно не это, то должен работать вот такой способ:
<link rel="stylesheet" href="data:text/css,html{font-size:99px}">То есть устанавливаем у тега HTML
/99/.test($('html').css('font')); // true, если есть поддержка dataURIPHP7
Всё, PHPNG отправился в «мастер», по всей видимости, это означает, что он появится сразу после версии 5.6, которую мы увидим буквально на днях (я уже немного писал о том, что в ней нового).
PHPNG — новая версия интерпретатора, которая, по всей видимости, будет носить номер семь, чтобы избежать путаницы с PHP6 — неудавшейся попыткой перевести ПХП на Юникод, которую широко в своё время освещали, но сообщество решило от неё отказаться — большинство разработчиков англоязычны и выгоды от этого трудоёмкого процесса не видели.
Новая версия ПХП интересна переработанными внутренностями, изменение которых связано со скоростью. Установленный «ВордПресс» работает на новой версии в два раза быстрее, что впечатляет, конечно. Плохая новость — придётся переделать бо́льшую часть модулей (если не вообще все). Процесс этот сейчас движется, но только для модулей, входящих в язык, несколько модулей (в том числе важный для меня OCI8) пока ещё не поправлены.
Думаю, что миграция авторских модулей произойдёт довольно быстро.
Странное с Флешем
Я с «Флешем» в последние годы не сталкивался, но и прежде подобного поведения не наблюдал. В общем, решил я вчера обновить в нашем продукте библиотеку ВебКамДжиЭс, она у нас для получения фотографии с камеры используется. Прежняя была 0.9, а тут я случайно обнаружил, что релиз вышел.
В прежней изображение с камеры получалось через Флеш, а релизная умеет использовать АПИ браузера, если таковое имеется. Правда, в «Сафари», которым я пользуюсь, этого АПИ нет и всё равно используется Флеш. Вот
В принципе, можно было откатиться на предыдущую версию, но смущало, что
У нас сервер отдавал Флеш с типом
Беглое гугление света не пролило, но если столкнётесь с похожей проблемой, имейте ввиду, возможное решение — смена типа для отдаваемого
Короткий способ определять IE
А какой сейчас самый короткий способ определять Эксплорер? Я понимаю, что привязываться к браузеру нехорошо, но у меня конкретная задача, которую никак не обойти — нужно проиграть звук в браузере (PCM wave), а ИЕ на запрос «сможешь проиграть?» к тегу AUDIO, говорит maybe. Т. е. давайте сюда свой звук, может и смогу.
Все короткие способы, которые раньше работали (условная компиляция, сравнение вертикального пробела с буквой v, хитрый хак с массивом и прочее)
В общем, я пока остановился на такой версии:
if (top.VBArray) {
// IE
} else {
// остальные браузеры
}Но буду признателен
Передача массива как аргументов в конструктор
Одна из вещей,
$result = call_user_func_array('myfunc', $args); // старый синтаксис
$result = myfunc(...$args); // PHP 5.6+Но помимо приятности, он решает две проблемы.
switch (count($params)) {
case 0:
$instance = new $class();
break;
case 1:
$instance = new $class($params[0]);
break;
case 2:
$instance = new $class($params[0], $params[1]);
break;
// и так далее…
default:
$refClass = new \ReflectionClass($class);
$instance = $refClass->newInstanceArgs($params);
break;
}В новом же синтаксисе код невероятно упрощается:
$instance = new $class(...$params);А, кроме того, становится более явным и простым. Так что очень радуюсь этому новому синтаксису.
Use SVG
Поскольку «Макбук» у меня с «ретиной», я давно озаботился тем, чтобы продукт, разработкой которого я управляю, хорошо смотрелся и на «ретине» тоже. Я считаю за такими экранами будущее и не грех позаботиться об этом будущем уже в настоящем. Если уж и не внедрить везде, так хоть научиться верстать.
Одним из главных помощников в такой вёрстке является, без сомнения, формаг СВГ — формат векторной графики, который на данный момент поддерживают все распространённые браузеры (включая ИЕ, с девятой версии). Особенно приятно, что его можно включать прямо внутрь ХТМЛя, это очень удобно — если СВГ используется для кучи мелких значков на странице, то не будет лишних соединений с сервером — ведь всё грузится вместе с основным кодом.
Но встаёт другая проблема — проблема повторного использования. Если одни и те же значки присутствуют на странице в изобилии, страница будет распухать, что печально.
К счастью, есть выход. СВГ — гибкая штука, в этом формате есть возможность использования частей изображения повторно. Первым шагом нужно определить
<svg style="display:none">
<symbol id="folder" viewBox="0 0 49.833 67.167">
<!-- код не рабочий, только для примера -->
<path d="M15.938,67.…"/>
<path d="M3.59,30.531c…"/>
</symbol>
<symbol id="file" viewBox="0 0 49.833 67.167">
<path d="M12.233c0.…"/>
<path d="M3.28C2.76…"/>
</symbol>
</svg>Тут у нас два изображения — folder и file, на которые мы можем сослаться следующим образом:
<svg width="28" height="37">
<use xlink:href="#folder"/>
</svg>
<svg width="28" height="37">
<use xlink:href="#file"/>
</svg>Таких вставок может быть сколько угодно и все они будут использовать одни и те же изображения, описанные
svg.green {
fill: green;
}
svg.red {
fill: red;
}Классы можно повесить на ссылающиеся теги СВГ, жаль только этот способ полон ограничений. Кроме как в ФФ более избирательные селекторы использовать не удаётся — всё, что ниже тега use для таблицы стилей как будто не существует. Если и есть
Язык программирования ГАРФ
Из случайно оборонённого комментария к одной из статей в интернете, узнал о существовавшем
Информации о языке очень мало, наиболее полное упоминание в рунете я нашёл в статье, которая так и называется — «Илья Шмаин»:
Это больше, чем язык программирования, это некоторый способ, стиль программирования вместе с математической теорией этого стиля. Это программирование на основе рекурсивных функций, опирающееся на классические работы Клини и одно их обобщение, сделанное
Есениным-Вольпиным. ГАРФ — это язык функционального программирования. Но он намного более абстрактен и универсален, чем первые языки такого типа. На нем можно было определять произвольные типы объектов и произвольные (но вычислимые) функции и функционалы на этих объектах.
- Точное описания синтаксиса и семантики. При этом синтаксис языка не фиксировался жестко. Описывалась «глубинная структура» и ее отображения в разные «поверхностные структуры», т. е. программист в принципе мог выбирать удобный для него синтаксис программы. Описывалась
теоретико-модельная семантика языка и вычисления, соответствующие этой семантике.
- Упомянутые выше
«индуктивно-определяемые области» содержали, в частности, идею того, что позже стало называтьсяобъектно-ориентированным програмированием (или абстрактными типами данных). Т. е. язык содержал средства для задания любых типов объектов с заданными на них функциями и отношениями, а потом уже на такого рода областях задавалась сама программа.
- То, что в ГАРФе «и функции, и области понимаются в самом широком смысле», означало, что можно было задавать любые функционалы, причем их определение могло содержать параметры (тем самым тип функций и объектов не фиксировался жестко).
В общем, ничего конкретного в статье нет, а жаль — упомянутое интересно, любопытно было бы взглянуть!
Chrome (but not Safari) CSS hack
Обратил внимание, что половина интернета сбилась с ног в попытках найти стильевой хак для «Хрома», который не срабатывал бы в «Сафари». Неясно почему его ещё никто не придумал, у меня на это ушла пара минут:
@supports (top: 0__qem) {
.chrome-only {
border: 1px solid #000; /* стиль только для Chrome (не Safari) */
}
}Работает начиная
Эта стандартная конструкция предназначена для проверки — поддерживает ли браузер указанное свойство. Недостаточно подставить туда свойство с префиксом webkit — сейчас многие браузеры читают такие свойства как свои,
Чтобы отсечь лишнее, я использую нестандартную единицу измерения Вебкита — «__qem», я о ней писал несколько лет назад. Вместе получается, что стиль в «Сафари» не срабатывает, потому что тот не поддерживает «@supports», а в остальных браузерах — потому что они не поддерживают «__qem».
Естественно, обёртки над Вебкитом (новая «Опера», браузер «Яндекса» и прочее) стиль увидят — но на то они и обёртки.
Случайные числа в Sed
После шахмат на «Седе» я, похоже, в глазах общественности стал
В основном, задачи очень простые и даже рутинные, ничего интересного, но вот вчера прислали действительно интересный вопрос. Вопрос в том можно ли командами «Седа» получить случайное число?
Уж не знаю что именно такого спросивший собирается с ним потом делать, надеюсь,
Итак, что же можно сделать? Команды жёстко заданы и менять их нельзя, возможно
Я
Тогда я не обратил на это должного внимания, а когда получил упомянутое письмо — вспомнил. Общая идея оформилась быстро: нужно очистить экран, скрыть курсор, выставить цвет фона и шрифта одинаковыми (чтобы скрыть прочитанный из файла мусор — ведь sed выведет прочитанное на экран), прочитать файл случайных чисел, потом сосчитать позицию в которой оказался курсор, восстановить параметры экрана и вывести позицию курсора.
В манипуляциях с экраном ничего сложного нет — существуют специальные управляющие последовательности при помощи которых это делается, даже координаты курсора можно получить ими же.
Получившийся код вполне уверенно работает, разве что у чисел, которые выводятся, распределение далековато от равномерного, но алгоритмы, исправляющие этот недостаток, в изобилии есть в интернете. Да и сам алгоритм получения случайного числа можно изменить: например не считывать позицию курсора,
В общем, у меня вышел вот такой код:
#!/usr/local/bin/gsed -n -f
# Очищаем экран, выставляем цвет чёрное на чёрном, убираем курсор
1i\\x1B[2J\x1B[f\x1B[30;40m\x1B[?25l
# Читаем файл случайных чисел, пока не встретим перевод строки
1R /dev/urandom
# Запрашиваем текущие координаты курсора
1a\\x1B[6n
2{
# Смотрим чему равна первая (Y) координата, оставляем только её
s/.*\x1B\[\([0-9]*\);1R.*/Random number: \1/
# Убираем текущий текст в буфер
h
# Очищаем экран, восстанавливаем цвет и курсор
i\\x1B[2J\x1B[f\x1B[0m\x1B[?25h
# Вынимаем из буфера сохранённое
g
# Печатаем сохранённое на экран
p
# Выходим
q
}У него есть и недостаток — так как «Сед» всегда совершает действия только после того как получит данные снаружи, вам придётся дважды нажать «Энтер», чтобы получить ожидаемое. Но от этого уже никуда не денешься, разве что можно сократить количество нажатий до одного — если отказаться от считывания координат.
Чудно́е в PHP
На работе сейчас с любопытным багом в ПХП разбирались. Разобрались, вот выжимка:
interface I {}
var_dump(class_exists('cI')); // false
var_dump(class_exists('C')); // true
class cI implements I {}
class C {}
var_dump(class_exists('cI')); // true
var_dump(class_exists('C')); // trueВыходит, если класс реализует интерфейс, то его не существует выше строки, где он декларируется, класс без реализации интерфейса виден в любом месте. Версия ПХП — 5.5.11. Я для интереса попробовал на 5.6а1 (есть под рукой собранный) — работает так же.
CSS variables
Константы в ЦСС (которые
Идея замечательная — если у вас есть
:root {
--bgcolor: #000;
}
body {
background-color: var(--bgcolor);
}Как видите, префикс «var» сменился на два минуса в полном соответствии с изменениями в стандарте. Чем не устроил предыдущий синтаксис мне не интересно, но этот префикс набирать определённо быстрее —
IE9 и таблицы

В продукте, которым я занимаюсь, минимальная версия Эксплорера, которую я решил поддерживать — девятая. В будущем я хочу поднять это требование до десятой, а пока приходится жить с тем, что есть — клиенты не готовы к резким переменам и я их понимаю.
Наткнулись на странную проблему в этой версии Эксплорера — некоторые таблицы выглядят как на снимке выше — ячейки в строках иногда выпирают вправо безо всякой видимой причины.
Дело оказалось в пробелах между ячейками — верстальщик отформатировал шаблон, чтобы он лучше читался, и все браузеры всё показали правильно, один девятый Эксплорер сошёл с ума. Лечится проблема запросто — либо убираем форматирование в шаблоне, либо убираем его прямо в браузере.
Я выбрал второй путь. Пусть лучше шаблон выглядит читаемо, а костыль для девятого Эксплорера всегда можно будет убрать лёгким движением руки. Выглядит он вот так, кстати (используется jQuery, само собой):
$("table tr").contents().filter(function() {
return this.nodeType == 3;
}).remove();И и И (логические выражения в PHP)
В ПХП есть два набора команд булевой логики — многим более привычные «&&» и «||», но кроме них — напоминающие о Бейсике и Эскюэле «and» и «or». Даже программирующие на языке годами часто не знают чем они отличаются, между тем употребление второго набора может привести к трудноуловимым багам, потому что ведут они себя чуть иначе.
Вся разница заключается в приоритетах выполнения, большинство языков, разбирая выражение, опирается на приоритеты выполнения, например, умножение приоритетней сложения, этому нас учат в школе, в ПХП и многих других языках — так же: 2 + 2 × 2 = 6, а не 8.
Так вот. Набор «and» и «or» — чуть ли не самые низкоприоритетные из всего, что может встретиться, а «&&» и «||» находятся
// какая-то переменная, значение которой в коде,
// к этому моменту, стало true
$check = true;
// а это другая контрольная переменная, где-то в коде
// она приобрела значение false
$criteria = false;
// и вот мы подходим к главному
$ret_bool = $criteria || $check;
$ret_logic = $criteria or $check;
var_dump($ret_bool, $ret_logic);Будут ли отличаться значения переменных ret_bool и ret_logic после выполнения программы? Конечно будут, иначе я бы не писал этут статью, неправда ли? Значение первой будет true, второй — false.
Почему так? Приоритеты! ПХП, исполняя предпоследние две строчки кода видит их
$ret_bool = ($criteria || $check);
($ret_logic = $criteria) or $check;Исполнение идёт строго по приоритетам, так как во втором случае операция присваивания имеет больший приоритет, чем «or», первым выполняется именно она, а проверка переменной check осуществляется, но её результат отбрасывается.
Это отлично подходит для программировая в «хакерском» стиле:
$ret_logic = $criteria or CheckSomething();Функция CheckSomething() вызовется только в том случае, если criteria будет равна false или эквиваленту, но зато результат функции никак не повлияет на значение ret_logic.
Впрочем, я такой способ программирования крайне не рекомендую — для современных программистов он сложен.
Свёртка в вебе
Я
<style>
.edge {
-webkit-filter: url(#edge);
filter: url(#edge);
}
</style>
<svg style="width: 0; height: 0">
<filter id="edge">
<feConvolveMatrix kernelMatrix="-1 -1 -1 -1 9 -1 -1 -1 -1" />
</filter>
</svg>
<image src="sample.jpg" class="edge">Магические числа в матрице — специальное ядро.
Впрочем, и раньше можно было внедрить в ХТМЛ
С таким возможностями, немудрено, что онлайновых «фотошопов» развелось, как грязи. Немного помучавшись с консолью, можно найти способ как поменять матрицу и это тоже будет работать! Например, вот так меняется число по центру (где у меня в примере девятка записана):
var arr = document.getElementById('edge').childNodes;
for (var i = 0; i < arr.length; i++) {
if (arr[i].kernelMatrix) {
arr[i].kernelMatrix.baseVal.getItem(4).value = newvalue;
break;
}
}Осталось только соединить этот код с «крутилкой» и готов