Архив рубрики: php

Отправка 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);

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

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

PHP Data Structures

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

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

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

Разработка приложения с помощью 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

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

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

array_map(‘intval’, $array) vs. array_map(function($val) { … }, $array)

И я продолжаю серию своих никчемных исследований…

<?php
ini_set('memory_limit', '4096M');

$array = [];
for ($i = 0; $i < 1000000; $i++) {
    $array[$i] = (string)$i;
}

$start = microtime(true);
$tmp = array_map('intval', $array);
$end = microtime(true);
echo 'array_map(\'intval\', $array): ' . ($end - $start) . PHP_EOL;

$start = microtime(true);
$tmp = array_map(function($value) {
    return (int)$value;
}, $array);
$end = microtime(true);
echo 'array_map(function($value) { ... }, $array): ' . ($end - $start) . PHP_EOL;
array_map('intval', $array): 0.47099804878235
array_map(function($value) { ... }, $array): 2.9696278572083

array_key_exists vs. isset

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

Код и результаты, как водится, ниже.

<?php

$array = range(0, 1000000, 3);

$start = microtime(true);
for ($i = 0; $i < 1000000; $i++) {
    array_key_exists($i, $array);
}
$end = microtime(true);
echo 'array_key_exists: ' . ($end - $start) . PHP_EOL;

$start = microtime(true);
for ($i = 0; $i < 1000000; $i++) {
    isset($array[$i]);
}
$end = microtime(true);
echo 'isset: ' . ($end - $start) . PHP_EOL;
array_key_exists: 0.13523197174072
isset: 0.055444002151489

Обновление PHP в Mac OS X

Сегодня мне рассказали про очень простой способ обновить PHP на локальной Mac OS X.

Шаг 1. Сначала необходимо установить Xcode, если вы этого до сих пор не сделали. Запускаем его, идем в меню XcodePreferences… и выбираем тут закладку Downloads. Жмем «Install» напротив Command Line Tools. Шаг 1 завершен, в дальнейшем при очередном обновлении этого делать уже не потребуется.

Шаг 2. Снова, если не было это сделано ранее, устанавливаем Homebrew. Для этого достаточно выполнить команду:
ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"

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

Шаг 3. Переходим непосредственно к установке новой версии PHP. По адресу http://php-osx.liip.ch есть небольшой сайтик посвященный утилите для установки PHP. Смотрим раздел «One Line Installation» и копируем оттуда команду, соответствующую желаемой версии интерпретатора. Например, для PHP 5.4 это будет:
curl -s http://php-osx.liip.ch/install.sh | bash -s 5.4

Сегодня он установил мне PHP 5.4.15, который является самой последней версией в данной ветке.

Тут необходимо заметить, что эта утилитка не заменяет установленный в системе PHP, поэтому по умолчанию будет работать старая версия. Чтобы это исправить нужно отредактировать (или создать) файл .profile, который находится в вашей домашней директории (то есть ~/.profile). В этот файл нужно добавить строчку, которая изменит путь поиска бинариков:
export PATH=/usr/local/php5/bin:$PATH

После этого нужно перезапустить терминал (или перелогиниться в нем), чтобы изменения вступили в силу.

function_exists vs. extension_loaded

Одно небольшое бесполезное исследование провел сегодня на работе по долгу службы. Код и результаты его выполнения ниже.

<?php

$functionExistsStart = microtime(true);
for ($i = 0; $i < 1000000; $i++) {
    function_exists('this_function_doesnt_exist');
}
$functionExistsEnd = microtime(true);

$extenstionLoadedStart = microtime(true);
for ($i = 0; $i < 1000000; $i++) {
    extension_loaded('this_extension_is_not_loaded');
}
$extenstionLoadedEnd = microtime(true);

echo 'function_exists ' .
    ($functionExistsEnd - $functionExistsStart) .
    PHP_EOL;

echo 'extension_loaded ' .
    ($extenstionLoadedEnd - $extenstionLoadedStart) .
    PHP_EOL;
function_exists 0.35822796821594
extension_loaded 0.40138292312622

Zend Certified Engineer

Итак, давно обещал рассказать, как сдавал экзамен. Руки как не доходили, так до сих пор и не доходят. Периодически через Желтые страницы Zend’a мне задают некоторые вопросы о самом экзамене.

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

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

В-третьих, Study Guide не дает всего объема информации, а лишь освещает все тематики, которые встречаются на экзамене. У меня самого с подготовкой проблем не возникло, так как я и до этого прочитал огромное количество книг и статей. Study Guide помог просто все в памяти поднять. Кстати, его бы я больше советовал тем, кто язык изучает. Как бы мне хотелось, чтобы первая книга по PHP была бы именно она!

Если вспомню еще что-то, обязательно сюда добавлю.