Отправка Push-уведомлений для iOS на PHP

Нужно было отправлять уведомления на iOS устройства. Мануалов и библиотек для этого море, но большинство из них перегружено. Поэтому решил написать сам. Посмотрел пару мануалов и библиотек и написал. Написал, а не работает!

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

$deviceToken = '7302abdc93d5f8c05e78b10b6c3b6a36b3bffb9dce551164000ddffd1fd23ead';
$certPath = __DIR__ . '/push_production.pem';

$message = 'Hello Dmitry';
$badge = 3;
$sound = 'default';

$payload = array();
$payload['aps'] = array('alert' => $message, 'badge' => (int) $badge, 'sound' => $sound);
$payload = json_encode($payload);

$apns_url = 'gateway.push.apple.com';
$apns_cert = $certPath;
$apns_port = 2195;

$stream_context = stream_context_create();
stream_context_set_option($stream_context, 'ssl', 'local_cert', $apns_cert);

$apns = stream_socket_client('ssl://' . $apns_url . ':' . $apns_port, $error, $error_string, 2, STREAM_CLIENT_CONNECT, $stream_context);

$apns_message = chr(0) . chr(0) . chr(32) . pack('H*', str_replace(' ', '', $deviceToken)) . chr(0) . chr(strlen($payload)) . $payload;
fwrite($apns, $apns_message);

@socket_close($apns);
@fclose($apns);

К сожалению, сейчас не могу вспомнить, где взял его. Отличие от оригинала тут совсем небольшое.

Запустил — пришло уведомление. Вроде код тот же, что и везде, а потом обратил внимание на упаковку сообщения.  В общем, для тех, кто мучается с той же проблемой, вот решение :) За кусок кода не бейте, пожалуйста, он не мой :)

Курсы по JavaScript

JavaScript я использую давно, не могу назвать точную дату. Года с 2000, наверное. Но никогда не пытался особо в нем разобраться. То ли терпения не хватало, то ли мотивации. Довольствовался базовыми знаниями, которых хватало для примитивного решения поставленной задачи.

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

И записался на курс по JavaScript Ильи Кантора. Курс хороший. Начинается с базовых вещей и дает хорошую основу. Не могу сказать, что узнал много нового. Будто все уже читал это, слышал и видел. Но благодаря курсу все встало на свои места. Илья уделял много внимания особенностям языка и его реализаций, показал, как нужно использовать язык, какие инструменты стоит использовать. Кроме того, тут произошло знакомство с ES6, который, несомненно, стоит использовать! Спасибо, Илья, за те два месяца! :)

certificate

PHP Data Structures

Очень понравилась эта презентация. Если встретите видео этого доклада, буду очень благодарен за ссылку.

Ну, и ссылочка на «пятничную» презентацию, которая заставила чуть ли не по полу кататься: WAT. A lightning talk by Gary Bernhardt from CodeMash 2012

Проверка адреса электронной почты

Обычно в коде мы проверяем адрес электронной почты только с помощью регулярки. Но этого не всегда бывает достаточно.

У меня возникла проблема с Amazon SES, когда я по старой базе данных решил сделать небольшую рассылку. После отправки всего 5000 писем у меня bounce-rate поднялся почти до 40% и мой аккаунт заблокировали.

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

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

Класс доступен на github.com и phpclasses.org.

MySQL 5.6 Developer

Сегодня я успешно сдал экзамен MySQL 5.6 Developer 1Z0-882. И теперь жду, когда же мне присвоят Oracle Certified Professional, MySQL 5.6 Developer.

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

Сначала прочитал MySQL 5.0 Certification Study Guide (купил ее два года назад, тогда собирался сдавать соответствующий экзамен). Потом порылся немного в интернете и нашел два очень полезных поста:

Ну, и далее мануал, мануал, мануал.

Из рекомендаций:

  • обращайте внимание на детали синтаксиса и особенности поведения
  • находите и проходите тесты
  • больше практики
  • хорошо спите, особенно накануне экзамена

Upd. Мой сертификат (PDF).

json_encode vs. serialize

Итак, продолжение серии бесполезных исследований. В этот раз мы сравниваем несколько разные по сути функции, которые, тем не менее, часто используются с одной целью. В этом исследовании мы хотим представить массив значений (не объектов PHP) в качестве строки. Это исследование инициировал на работе Фархад Вильданов. К сожалению, ему это не помогло, так как он имел дело как раз с массивом объектов PHP.

Что нам скажет serialize()?

<?php
ini_set('memory_limit', '4G');

$initialArray = array_fill(0, 1000000, ['test value']);
$start = microtime(true);
$serializedA = serialize($initialArray);
$end = microtime(true);

echo 'serialize() string length: ' . strlen($serializedA) . PHP_EOL;
echo 'serialize() time: ' . ($end - $start) . PHP_EOL;
echo 'serialize() memory: ' . memory_get_peak_usage(true) . PHP_EOL;

Вывод:

serialize() string length: 36888902
serialize() time: 0.43295097351074
serialize() memory: 327155712

Так выглядит тест для json_encode():

<?php
ini_set('memory_limit', '4G');

$initialArray = array_fill(0, 1000000, ['test value']);
$start = microtime(true);
$json = json_encode($initialArray);
$end = microtime(true);

echo 'json_encode() string length: ' . strlen($json) . PHP_EOL;
echo 'json_encode() time: ' . ($end - $start) . PHP_EOL;
echo 'json_encode() memory: ' . memory_get_peak_usage(true) . PHP_EOL;

Вывод:

json_encode() string length: 15000001
json_encode() time: 0.19829201698303
json_encode() memory: 127401984

DevConf 2014

Последние несколько лет я стараюсь посещать конференции для разработчиков. Это дело занятное и порой даже полезное. 14 и 15 июня в Москве проходила конференция DevConf, которая объединяет несколько разных направлений в разработке — как по языкам программирования, так и по назначению — веб, мобайл. DevConf я посещаю уже третий год подряд. Должен заметить, что с каждым посещением какой-либо конфы я в них несколько разочаровываюсь. Этот случай — не исключение.

Да, было несколько интересных докладов. На второй день я даже нашел пару полезных мастер-классов.

Суть первого мастер-класса можно свести к тому, что для бекапов в MySQL используйте специализированные решения вроде MySQL Enterprise Backup (как минимум за $5000 в год) или же Percona XtraBackup. Многое умеют обе утилиты, но некоторые вещи несколько по-разному.

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

С первого же дня даже отметить особо нечего.

Посещал доклады:

Иногда возникает вопрос — есть ли вообще смысл ездить на конференции и тратить на это деньги (свои или компании) и время? Я считаю, оно того стоит. Полезные и интересные доклады бывают неожиданностью, но такой приятной. Пользы от них очень много. Кроме того, всегда интересно повстречать много-много разработчиков, каждый из которых несет за спиной свой багаж опыта и порой не прочь поделиться.

Разработка приложения с помощью Zend Framework 2

1. Приложение необходимо разбивать на модули. Модуль включает в себя какую-то сущность и связанные с ней действия. Например, это может быть пользователь и контроллеры, отвечающие за вход/регистрацию, личный кабинет (настройки) и управление пользователями. Раньше я, разумеется, использовал модульность, но, как правило, модуль был достаточно весомым, он включал хоть и независимую (относительно) часть приложения, но все же там могли оказаться, например, и пользователи, и форум. Предложенная модель модулей в ZF2 предполагает соблюдения принципа единственной ответственности на более высоком уровне (по сравнения с классом или методом).

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

3. Dependency Injection — об этом много и давно говорится на просторах интернета. Я рассматриваю DI с точки зрения удобства модульного тестирования, когда любую зависимость можно легко подменить моком. Например, при тестировании класса, взаимодействующего с базой данных, вам не хочется делать реальные запросы, так как вы тестируете в данный момент вовсе не это.  С другой стороны DI делает код более гибким, когда изменить используемый алгоритм не составит труда — нужно лишь передать новый объект, который будет реализовывать тот же интерфейс. Например, для создания пароля вам захочется изменить алгоритм на более надежный.

autotest для PHP

Я последнее время немало человеко-часов провел в экспериментах с Руби и Рельсами (об этом я еще напишу отдельный пост). Вернувшись назад в мир разработки на PHP мне стало очень не хватать одной простой утилиты, к которой я успел так привыкнуть. Называется она autotest. Проблема в том, что она для руби. Я быстренько поискал в Гугле и на Гитхабе и не нашел ни одного решения на PHP (позже все же нашел несколько, но они мне все равно не понравились). А я верю в то, что инструменты для разработки на каком-то языке должны писаться на нем же. Хотя бы потому, что ты как разработчик на этом языке не будешь испытывать проблем, если потребуется что-то на скорую руку исправить или модифицировать. Тут руки зачесались, и я написал свой вариант автотеста.

Прошу любить и жаловать — php.autotest.

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

sudo ln bin/autotest.php /usr/bin/autotest

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

Использование не сложнее установки. Просто перейдите в корень приложения и запустите автотест:

autotest

Он не выведет никаких сообщений, а просто начнет молчаливо ждать изменений в коде приложения или тестов. Как только изменения будут замечены, сразу же будут запущены соответствующие тесты. Как скоро автотест заметит изменения зависит от настройки таймаута, по умолчанию это 1 секунда. Но это время всегда можно изменить с помощью опции timeout:

autotest --timeout=5

Так он будет проверять изменения раз в 5 секунд.

Есть еще несколько полезных опций для не совсем «стандартных» проектов. Например, кода несколько иначе называются директории с кодом или тестам (по умолчанию считается, что для кода — src, для тестов — tests). Или же когда надо указать команду для phpunit.

autotest --src_path=sources --tests_path=testing --cmd=/usr/local/zend/bin/phpunit

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

Надеюсь, утилита будет кому-то полезной.

Как отправить процесс в фоновый режим?

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

Но на деле все гораздо проще.

Нажимаем Ctrl + Z (приостановить процесс) и набираем:

bg

Готово. Теперь процесс продолжил выполнение в фоновом режиме.