Вкратце, push-уведомления — это небольшие по объему важные сообщения от программы или сервиса, отображаемые операционной системой тогда, когда вы непосредственно не работаете с указанным приложением или сервисом. Преимущество таких уведомлений в отсутствии необходимости держать программу вечно в памяти, тратя на нее процессорные мощности и память.
Не буду здесь расписывать всю технологию доставки удаленного уведомления, ибо это уже сделано до меня. Выглядит примерно так: периодически демон опрашивает сервер и в случае появления сообщения, показывает его нам.
Для iOS придумали APNS, для Android-а — C2DM-GCM, я же хочу рассказать про кроссплатформенный (громко) сервис Pushover и связке его с php-сайтом.
Пример задачи
Предположим, что раз в день мы хотим знать что-либо о количестве заказов на сайте за день и их стоимости.
Сайт крутится на некоторой CMS на PHP и mySQL, принимающая сторона имеет стильные iPhone и Android-телефоны.
Срочность доставки сообщения не относится к жизненно-важным показателям.
Надо найти условно безгеморройное решение.
Pushover
Pushover — это скромный сервис уведомлений, а также приложения для iOS и Android, планируются поделки и для BlackBerry и OS X Mountain Lion. Сервис имеет свой API, позволяет отправлять бесплатно до 7.5 тысяч сообщений в месяц.
Сообщение, помимо основного текста сообщения длиной 512 символов, может содержать крупный заголовок, URL (тогда длина сообщения увеличивается до 500) и его тайтл (все отображается отдельными сформированными блоками, потому такое разграничение). Сообщение можно доставить под неким выбранным указанным приоритетом. Пользователь может указать «тихие» часы, когда его не стоить будить уведомления, а также подключать и отключать устройства, на которые будут приходить уведомления.
Уведомление может быть доставлено пользователям, предоставившим свой код, всем устройствам этого пользователя или по выбору. Для приема сообщения пользователю необходимо быть зарегистрированным в сервисе и обладать хотя бы одним рабочим устройством.
Добавление пользователя
После прохождения регистрации, каждый пользователь попадает в свой кабинет, где он сразу видит свой хэш-токен. Это уникальный идентификатор пользователя, на который в последствии и отправляются уведомления.
Пользователю, желающему принимать сообщения, необходимо поставить на свой телефон/планшет/абы что приложение из соответствующего магазина.
Добавление сервиса
Добавление сервиса ничуть не сложнее. Из личного кабинета надо перейти на страницу создания приложения, где предлагается описать продукт:
После заполнения соответствующих полей, нам становится известен токен приложения. В принципе, это все, что необходимо для отправки сообщения.
На странице приложения в последующем будет красивый график успешно отправленных сообщений.
Связывание приложений и получателей
… не выполняется никак. Любое приложение может отправить любому пользователю уведомление, если знает его токен. Прием токенов от населения остается на совести приложения. Также как и отписка от рассылок.
API
Небольшое, емкое и понятное. Для отправки сообщения в POST-запросе к api.pushover.net/1/messages.json или api.pushover.net/1/messages.xml минимально необходимо указать:
- token — хэш-токен вашего приложения или сервиса.
- user — хэш-токен пользователя, которому вы отправляете уведомление.
- message — текстовая часть сообщения.
Дополнительно к этому можно добавить:
- device — идентификатор устройства пользователя, дабы не отсылать уведомления сразу на все его устройства
- title — заголовок сообщения, если не указан, будет показано название сервиса
- url — ссылка на web-страницу, если в этом есть необходимость
- url_title — заголовок к ссылке
- priority — приоритет сообщения, ставится в 1 для высокого приоритета, обходящего все «тихие» часы и -1 для тихого уведомления.
- timestamp — UNIX метка времени, когда это уведомлениебыло создано. Расписания доставки сообщений сервисом не предусмотрено.
Ответ сервера
Ответ сервера будет представлен в json или xml формате (в зависимости от расширения вызываемого скрипта).
Если все прошло удачно будет отдан объект содержанием поля status, равном 1.
Иначе, поле status будет содержать нечто иное, а поле errors — массив описания ошибок. Вот примеры ответов удачной и неудачной отправок в формате XML:
<?xml version="1.0" encoding="UTF-8"?>
<hash>
<status type="integer">1</status>
</hash>
и
<?xml version="1.0" encoding="UTF-8"?>
<hash>
<token>invalid</token>
<errors type="array">
<error>application token is invalid</error>
</errors>
<status type="integer">0</status>
</hash>
PHP
На главной странице и в факе в разделах «смотрите, как легко!» приводятся коды простейшего скрипта на различных языках для отправки и есть ссылка на 3rd-party php-класс от Chris Schalenborgh.
Везде используется сURL, что впрочем, понятно.
Зафигарим свой класс
Куда ж нынче без велосипедов?
На самом деле, мне не слишком понравилось, что успех отправки уведомления определяется либо как true/false, либо выводится сразу вся простыня ответа сервера. Да вообще обработки ошибок нет. Считаю, что если посетителю сайта не обязательно, то разработчику надо знать, почему не отправлено то или иное сообщение.
В общем, существенно меняем все, классы уехали на GitHub.
class PushoverException extends Exception
{
/**
* Messages array
* @var array
*/
private $fMessages;
/**
* Exception constructor
* @param array $aMessages An array of messages
*/
public function __construct(array $aMessages)
{
parent::__construct('PushoverException exception');
$this->fMessages = $aMessages;
}
/**
* Get messages array
* @return array
*/
public function getMessages()
{
return empty($this->fMessages) ? array() : $this->fMessages;
}
}
class Pushover
{
/*
* Pushover json api service url
*/
const C_API_URL = 'https://api.pushover.net/1/messages.json';
/**
* Properties storage array
* @var array
*/
private $fProperties;
/**
* cURL instance
*/
private $fCurl;
//--------------------------------------------------------------------------//
/**
* Properties getter
* @param string $aPropertyName Property name
* @return mixed
*/
public function __get($aPropertyName)
{
if(array_key_exists($aPropertyName, $this->fProperties))
return $this->fProperties[$aPropertyName];
return null;
}
/**
* Properties setter
* @param string $aPropertyName Property name
* @param mixed $aValue Property value
*/
public function __set($aPropertyName, $aValue)
{
$this->fProperties[$aPropertyName] = $aValue;
}
//--------------------------------------------------------------------------//
/**
* Class constructor
* @param string $aApplicationToken Application token
*/
public function __construct($aApplicationToken = null)
{
if(!empty($aApplicationToken))
$this->applicationToken = $aApplicationToken;
$this->fCurl = curl_init();
curl_setopt($this->fCurl, CURLOPT_URL, self::C_API_URL);
curl_setopt($this->fCurl, CURLOPT_HEADER, false);
curl_setopt($this->fCurl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($this->fCurl, CURLOPT_SSL_VERIFYPEER, false);
}
/**
* Class destructor
*/
public function __destruct()
{
curl_close($this->fCurl);
}
//--------------------------------------------------------------------------//
/**
* Throws an exceprion with single message
* @param mixed $aMessage
* @throws PushoverException
*/
public function throwMessage($aMessage)
{
throw new PushoverException(array($aMessage));
}
/**
* Throws an exceprion with an array of messages
* @param array $aMessages
* @throws PushoverException
*/
public function throwMessages(array $aMessages)
{
throw new PushoverException($aMessages);
}
//--------------------------------------------------------------------------//
/**
* Send pushover notification
*/
public function send()
{
if(!strlen($this->applicationToken))
$this->throwMessage('Application token is empty');
if(!strlen($this->userToken))
$this->throwMessage('User token is empty');
if(!strlen($this->notificationMessage))
$this->throwMessage('Notification message is empty');
if(intval($this->notificationTimestamp) <= 0)
$this->notificationTimestamp = time();
$lSendParams = array(
'token' => $this->applicationToken,
'user' => $this->userToken,
'device' => $this->userDevice,
'title' => $this->notificationTitle,
'message' => $this->notificationMessage,
'priority' => $this->notificationPriority,
'timestamp' => $this->notificationTimestamp,
'url' => $this->notificationUrl,
'url_title' => $this->notificationUrlTitle
);
foreach($lSendParams as $lKey => $lParam)
if(empty($lParam))
unset($lSendParams[$lKey]);
curl_setopt($this->fCurl, CURLOPT_POSTFIELDS, $lSendParams);
$lResponseJson = curl_exec($this->fCurl);
if($lResponseJson === false)
$this->throwMessage('API request error');
$lResponse = json_decode($lResponseJson, true);
if(empty($lResponse) || !is_array($lResponse))
$this->throwMessage('Bad API response');
if(!empty($lResponse['errors']))
$this->throwMessages($lResponse['errors']);
if(empty($lResponse['status']) || intval($lResponse['status']) != 1)
$this->throwMessage('Unknown notification send error');
}
}
Минимальное сообщение теперь довольно просто отправить, ошибки можно разбирать:
$lPushover = new Pushover('Write application token here');
$lPushover->userToken = 'scecify user token';
$lPushover->notificationMessage = 'Notification message';
try
{
$lPushover->send();
echo '<font color="green">Message sent</font>', PHP_EOL;
}
catch (PushoverException $aException)
{
echo '<font color="red">Error sending messages</font><br>', PHP_EOL;
echo '<ul>', PHP_EOL;
foreach($aException->getMessages() as $lMessage)
echo '<li>', $lMessage, '</li>', PHP_EOL;
echo '</ul>', PHP_EOL;
}
Пользователь уже принял сообщение.
Итого
Знаем об удобном сервисе удаленных уведомлений, одинаково успешно передающий сообщения пользователям Android и iOS.
Имеем рабочий механизм отправки уведомлений с сайта на PHP.
Автор: Urvin