2011-03-04

Форматирование JSON


Сейчас работаю над проектом, в котором клиентские JavaScripts получают много JSON ответов от сервера. Я же занимаюсь разработкой back-end PHP скриптов и чтобы упростить жизнь своим коллегам по разбору JSON в браузерных консолях (будь то хоть Firebug, хоть Chrome), мне захотелось отдавать в debug режиме отформатированный читабельные JSON данные. Погуглив на эту тему, я сразу же натолкнулся на необходимую функцию, которой и хочу с вами сегодня поделиться:

/**
 * Indents a flat JSON string to make it more human-readable.
 * @param string $json The original JSON string to process.
 * @return string Indented version of the original JSON string.
 */
function indent($json) {

 $result = '';
 $pos = 0;
 $strLen = strlen($json);
 $indentStr = '  ';
 $newLine = "\n";
 $prevChar = '';
 $outOfQuotes = true;

 for ($i = 0; $i <= $strLen; $i++) {

  // Grab the next character in the string.
  $char = substr($json, $i, 1);

  // Are we inside a quoted string?
  if ($char == '"' && $prevChar != '\\') {
   $outOfQuotes = !$outOfQuotes;

   // If this character is the end of an element, 
   // output a new line and indent the next line.
  } else if (($char == '}' || $char == ']') && $outOfQuotes) {
   $result .= $newLine;
   $pos--;
   for ($j = 0; $j < $pos; $j++) {
    $result .= $indentStr;
   }
  }

  // Add the character to the result string.
  $result .= $char;

  // If the last character was the beginning of an element, 
  // output a new line and indent the next line.
  if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) {
   $result .= $newLine;
   if ($char == '{' || $char == '[') {
    $pos++;
   }

   for ($j = 0; $j < $pos; $j++) {
    $result .= $indentStr;
   }
  }

  $prevChar = $char;
 }

 return $result;
}

Эта функция сразу же была внедрена в проект и теперь коллегам разбирать JSON куда более удобно. :)

2011-02-20

Как работают сервисы типа "shorten URL"?

Не так давно мне понадобилось реализовать свою генерацию коротких URL. Существует масса сервисов по укорачиванию URL: bit.ly, is.gd... Продолжать можно очень долго.
Возник вопрос, а как же работает этот алгоритм? В коротком URL, как правило, могут присутствовать символы a-z, A-Z, 0-9, возможно, еще какие-то спецсимволы.

Таким образом, попытаемся представить, что на выходе у нас получается 62-ричное число. На вход же подается очень большая строка, которую также можно представить в виде N-ричного числа, где N, очевидно, заведомо больше 62.

Тут точного ответа по поводу, как именно работают эти сервисы, дать невозможно. Но можно предположить, что у них есть глобальный счетчик 10-ричных чисел, его числа они и переводят в 62-ричную систему счисления. И существует таблица, ставящая в соответствие URL - short URL. Таким образом, до 3 символов включительно могут соответствовать количеству URL:

62 * 62 * 62 = 238328

Применительно к практике, не всегда можно использовать столь небольшие числа. Например, при переводе MD5 хеша в 62-ричное число, разрядность числа превосходит разрядность регистров процессора и обычными инструкциями оперировать уже не получается. Поэтому на помощь приходит так называемая Длинная арифметика и модуль для PHP BC Math.

Немного погуглив, я натолкнулся на такую функцию, которая и выполняет всю работу (жаль, автор был немец и пришлось рефакторить названия переменных):

function bc_base_convert($value, $frombase, $tobase)
{
 $stock = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
 if (max($frombase, $tobase) > strlen($stock)) {
  trigger_error('Bad Format max: ' . strlen($stock), E_USER_ERROR);
 }
 if (min($frombase, $tobase) < 2) {
  trigger_error('Bad Format min: 2', E_USER_ERROR);
 }
 $decimal = '0';
 $level = 0;
 $result = '';
 $value = trim((string) $value, "\r\n\t +");
 $sign = '-' === $value[0] ? '-' : '';
 $value = ltrim($value, "-0");
 $len = strlen($value);
 for ($i = 0; $i < $len; $i++) {
  $temp = strpos($stock, $value[$len - 1 - $i]);
  if (false === $temp) {
   trigger_error('Bad Char in input 1', E_USER_ERROR);
  }
  if ($temp >= $frombase) {
   trigger_error('Bad Char in input 2', E_USER_ERROR);
  }
  $decimal = bcadd($decimal, bcmul(bcpow($frombase, $i), $temp));
 }
 if (10 == $tobase) {
  return $sign . $decimal;
 }
 while (1 !== bccomp(bcpow($tobase, $level++), $decimal)) {
 }
 for ($i = $level - 2; $i >= 0; $i--) {
  $factor = bcpow($tobase, $i);
  $number = bcdiv($decimal, $factor, 0);
  $decimal = bcmod($decimal, $factor);
  $result .= $stock[$number];
 }
 $result = empty($result) ? '0' : $result;
 return $sign . $result;
}

Первым параметром подается число (может быть и строка с MD5, например). Второй и третий параметры используются для указания из какой в какую систему счисления переводить число.
Массив $stock может быть модифицирован, например, если вы хотите использовать 63 или более -ричную систему счисления. Эта функция по умолчанию поддерживает переводы чисел в систему счисления до 62-ричных включительно.

Надеюсь, что смог помочь ищущим истину людям. :)

2011-02-19

Сравнение последних сборок браузеров в SunSpider

Сегодня решил снова провести тестирование браузеров в, наверное, всеми известном бенчмарке SunSpider. Были взяты следующие подопытные:
  • Mozilla Firefox 4.0 beta 12 pre (2011-02-18)
  • Google Chrome 11.0.672.2 beta
  • Opera 11.10 build 2005 alpha
  • Internet Explorer 9.0.8080.16413 RC
Safari в этот раз не вошел в результаты тестирования. Но я его также протестировал с целью нормализации последствий разгона своего процессора и памяти.
Тесты проводились в наиболее нейтральном и одинаковом рабочем окружении. Тестовая кофигурация:
  • Intel Core 2 Quad Q6600  @ 3.10GHz
  • DDR2 @ 516MHz (5-5-5-18)
Результаты тестирования:
 
Результаты тестирования браузеров в SunSpider 0.9.1
В этот раз перед моими глазами предстала довольно интересная картина. Несмотря на разгон процессора и памяти на моей машине (30% производительности, если сравнивать по старой версии Safari), результаты позволяют сделать вывод, что, например, Mozilla сделала свой браузер приблизительно в 1.2-1.5 раза быстрее. Эти 2 месяца разработчики браузеров не сидели просто так.
Впечатляют результаты IE9 в этом бенчмарке, хотя ходят слухи, что он просто заточен под быстрое прохождение этого теста.
Opera удалось обогнать Chrome, что должно послужить стимулом инженерам гуглобраузера. По сути, Chrome практически никак не сдвинулся в плане скорости исполнения JavaScript. И если так пойдет и дальше, то ему в спину скоро будет дышать даже неповоротливый некогда Firefox.
 
На этом все, будем ждать следующей итерации этапа развития современных браузеров.

2011-01-18

Множественные формы в Yii

Да, вам не показалось, в этот раз речь пойдет о PHP фреймворке Yii. И да простит меня домен третьего уровня... :)

Когда я работал с Zend Framework, мне очень понравилась их реализация множественных форм, о которых я уже когда-то рассказывал. Перейдя в свое время на Yii, я огорчился, узнав, что там подобной удобностью и не пахнет. Нет, я, разумеется, знал про CChoiceFormat, который позволял использовать множественные формы примерно следующим образом:

CChoiceFormat::format('n%10==1 && n%100!=11#раз|n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20)#раза|#раз', $data->views);

Но согласитесь, это ни разу не эстетично. И этой записи не было бы, если бы не случился выход Yii 1.1.6, в котором и появилась долгожданная и аккуратная, прописанная в файлах интернационализации, поддержка множественных форм. Теперь и в Yii все стало куда симпатичнее и "правильнее". Аналог вышеописанного использования теперь выглядит так:

Yii::t('yii', 'раз|раза|раз', $data->views);

Приятного вам кодинга! :)

2011-01-15

Очередные приколы imagick в PHP под Windows

Вроде бы ничего не предвещало беды, я уже рассказывал о своих похождениях с imagick под Windows. Но в нынешних проектах у моих коллег начали возникать проблемы с неработоспособностью этого расширения. Решив обновить всю свою сборку до актуальных версий, я столкнулся с аналогичными проблемами: 

Premature end of script headers, 500 ошибка возврата от Apache.

Проколупавшись в итоге несколько часов, я набрел на интересный комментарий на блоге Mikko. Для тех, кто не знает английский — с тех пор как ImageMagick (собственно, сам движок обработки изображений) стал собираться в Visual Studio 2010 (компилятором VC10), под PHP в Windows у всех проблемы. Последняя работоспособная версия ImageMagick, собранная VC9 — 6.6.4-1. Ее вы можете скачать по этому адресу:

http://image_magick.veidrodis.com/image_magick/binaries/ImageMagick-6.6.4-1-Q16-windows-dll.exe

Удалите все другие, установленные у вас версии и установите эту. Если вы разрабатываете на PHP в Windows, другие версии вам и не понадобятся. Но если вам вдруг потребуется какая-нибудь иная версия из архивов, то поищите ее по следующему адресу:

http://image_magick.veidrodis.com/image_magick/binaries/

В этот раз у меня был установлен PHP 5.3.5 non thread safe (VC9, x86) и аналогичное расширение imagick non thread safe (VC9, x86). Apache работал в режиме FastCGI. Больше ошибок с 500 статусом по поводу imagick у меня не возникает.

Так и в этот раз я победил imagick. :)

2010-12-14

SunSpider 0.9.1 на лезвиях

Мое предыдущее сравнение производительности браузеров Firefox оставляло желать лучшего. Визуально восприятие информации было просто ужасным. По прошествии некоторого времени мне опять захотелось посмотреть, кто же быстрее ныне в исполнении JavaScript. Я взял последние нестабильные (кроме Safari) версии популярных браузеров и прогнал на них SunSpider 0.9.1
Современные браузеры в бенчмарке SunSpider 0.9.1
Интересен тот факт, что Firefox серьезно отстает от своих основных конкурентов, но не стоит забывать, что этот браузер является наиболее удобным при разработке веб-приложений. Впереди планеты всей оказался Chrome от компании Google, которому в свою очередь успевает наступать на пятки Opera.

2010-09-18

Нововведения MySQL 5.5

Возможно, вы уже слышали о СУБД MySQL 5.5, которая находится еще в стадии разработки. В новой версии ожидается довольно впечатляющий список вкусных нововведений, о которых и пойдет речь. На своем домашнем сервере я использую последнюю стабильную версию 5.1 и очень жду выхода новой версии этой СУБД.
Итак, перейдем к деталям. Нас ожидает:
  1. Улучшена производительность на многоядерных процессорах. Теперь уже не те времена, когда процессорная мощь наращивалась частотами и можно было спокойно говорить: “Ждите, с новым процессором ваша СУБД станет быстрее”. Ныне разработка MySQL направлена на использование возможностей многоядерных процессоров. Основные усилия сосредоточены на улучшении производительности движка InnoDB и особенно блокировки таблиц.
  2. Подсистема ввода / вывода движка InnoDB оптимизирована для эффективного использования ресурсов устройств ввода / вывода.
  3. Улучшена производительность MySQL Server для ОС Solaris.
  4. Возможность просматривать более полную информацию о используемых системных ресурсах с помощью команды SHOW ENGINE INNODB STATUS.
  5. Поддержка стандартных для SQL: SIGNAL и RESIGNAL операторов.
  6. Поддержка новых многобайтовых кодировок: utf16, utf32, utf8mb4.
  7. Улучшена работа с XML, в том числе введен новый оператор LOAD XML.
  8. Добавлена функция TO_SECONDS(). Эта функция преобразует дату или дату и время в количество секунд, прошедшее с начала 0 года.
Также нас ждут улучшения в плане партицирования. Но, к сожалению, мой опыт не позволяет писать о них, поэтому я не стану вникать в подробности этих улучшений.