414 lines
9.5 KiB
PHP
414 lines
9.5 KiB
PHP
<?php
|
|
|
|
namespace LaravelFCM\Response;
|
|
|
|
use Monolog\Logger;
|
|
use Monolog\Handler\StreamHandler;
|
|
use Psr\Http\Message\ResponseInterface;
|
|
|
|
/**
|
|
* Class DownstreamResponse.
|
|
*/
|
|
class DownstreamResponse extends BaseResponse implements DownstreamResponseContract
|
|
{
|
|
const MULTICAST_ID = 'multicast_id';
|
|
const CANONICAL_IDS = 'canonical_ids';
|
|
const RESULTS = 'results';
|
|
|
|
const MISSING_REGISTRATION = 'MissingRegistration';
|
|
const MESSAGE_ID = 'message_id';
|
|
const REGISTRATION_ID = 'registration_id';
|
|
const NOT_REGISTERED = 'NotRegistered';
|
|
const INVALID_REGISTRATION = 'InvalidRegistration';
|
|
const UNAVAILABLE = 'Unavailable';
|
|
const DEVICE_MESSAGE_RATE_EXCEEDED = 'DeviceMessageRateExceeded';
|
|
const INTERNAL_SERVER_ERROR = 'InternalServerError';
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $numberTokensSuccess = 0;
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $numberTokensFailure = 0;
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $numberTokenModify = 0;
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @var
|
|
*/
|
|
protected $messageId;
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $tokensToDelete = [];
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $tokensToModify = [];
|
|
/**
|
|
* @internal
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $tokensToRetry = [];
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $tokensWithError = [];
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @var bool
|
|
*/
|
|
protected $hasMissingToken = false;
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @var array
|
|
*/
|
|
private $tokens;
|
|
|
|
/**
|
|
* DownstreamResponse constructor.
|
|
*
|
|
* @param \Psr\Http\Message\ResponseInterface $response
|
|
* @param $tokens
|
|
*/
|
|
public function __construct(ResponseInterface $response, $tokens)
|
|
{
|
|
$this->tokens = is_string($tokens) ? [$tokens] : $tokens;
|
|
|
|
parent::__construct($response);
|
|
}
|
|
|
|
/**
|
|
* Parse the response.
|
|
*
|
|
* @param $responseInJson
|
|
*/
|
|
protected function parseResponse($responseInJson)
|
|
{
|
|
$this->parse($responseInJson);
|
|
|
|
if ($this->needResultParsing($responseInJson)) {
|
|
$this->parseResult($responseInJson);
|
|
}
|
|
|
|
if ($this->logEnabled) {
|
|
$this->logResponse();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @param $responseInJson
|
|
*/
|
|
private function parse($responseInJson)
|
|
{
|
|
if (array_key_exists(self::MULTICAST_ID, $responseInJson)) {
|
|
$this->messageId;
|
|
}
|
|
|
|
if (array_key_exists(self::SUCCESS, $responseInJson)) {
|
|
$this->numberTokensSuccess = $responseInJson[self::SUCCESS];
|
|
}
|
|
|
|
if (array_key_exists(self::FAILURE, $responseInJson)) {
|
|
$this->numberTokensFailure = $responseInJson[self::FAILURE];
|
|
}
|
|
|
|
if (array_key_exists(self::CANONICAL_IDS, $responseInJson)) {
|
|
$this->numberTokenModify = $responseInJson[self::CANONICAL_IDS];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @param $responseInJson
|
|
*/
|
|
private function parseResult($responseInJson)
|
|
{
|
|
foreach ($responseInJson[self::RESULTS] as $index => $result) {
|
|
if (!$this->isSent($result)) {
|
|
if (!$this->needToBeModify($index, $result)) {
|
|
if (!$this->needToBeDeleted($index, $result) && !$this->needToResend($index, $result) && !$this->checkMissingToken($result)) {
|
|
$this->needToAddError($index, $result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @param $responseInJson
|
|
*
|
|
* @return bool
|
|
*/
|
|
private function needResultParsing($responseInJson)
|
|
{
|
|
return array_key_exists(self::RESULTS, $responseInJson) && ($this->numberTokensFailure > 0 || $this->numberTokenModify > 0);
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @param $results
|
|
*
|
|
* @return bool
|
|
*/
|
|
private function isSent($results)
|
|
{
|
|
return array_key_exists(self::MESSAGE_ID, $results) && !array_key_exists(self::REGISTRATION_ID, $results);
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @param $index
|
|
* @param $result
|
|
*
|
|
* @return bool
|
|
*/
|
|
private function needToBeModify($index, $result)
|
|
{
|
|
if (array_key_exists(self::MESSAGE_ID, $result) && array_key_exists(self::REGISTRATION_ID, $result)) {
|
|
if ($this->tokens[$index]) {
|
|
$this->tokensToModify[$this->tokens[$index]] = $result[self::REGISTRATION_ID];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @param $index
|
|
* @param $result
|
|
*
|
|
* @return bool
|
|
*/
|
|
private function needToBeDeleted($index, $result)
|
|
{
|
|
if (array_key_exists(self::ERROR, $result) &&
|
|
(in_array(self::NOT_REGISTERED, $result) || in_array(self::INVALID_REGISTRATION, $result))) {
|
|
if ($this->tokens[$index]) {
|
|
$this->tokensToDelete[] = $this->tokens[$index];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @param $index
|
|
* @param $result
|
|
*
|
|
* @return bool
|
|
*/
|
|
private function needToResend($index, $result)
|
|
{
|
|
if (array_key_exists(self::ERROR, $result) && (in_array(self::UNAVAILABLE, $result) || in_array(self::DEVICE_MESSAGE_RATE_EXCEEDED, $result) || in_array(self::INTERNAL_SERVER_ERROR, $result))) {
|
|
if ($this->tokens[$index]) {
|
|
$this->tokensToRetry[] = $this->tokens[$index];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @param $result
|
|
*
|
|
* @return bool
|
|
*/
|
|
private function checkMissingToken($result)
|
|
{
|
|
$hasMissingToken = (array_key_exists(self::ERROR, $result) && in_array(self::MISSING_REGISTRATION, $result));
|
|
|
|
$this->hasMissingToken = (bool) ($this->hasMissingToken | $hasMissingToken);
|
|
|
|
return $hasMissingToken;
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* @param $index
|
|
* @param $result
|
|
*/
|
|
private function needToAddError($index, $result)
|
|
{
|
|
if (array_key_exists(self::ERROR, $result)) {
|
|
if ($this->tokens[$index]) {
|
|
$this->tokensWithError[$this->tokens[$index]] = $result[self::ERROR];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
protected function logResponse()
|
|
{
|
|
$logger = new Logger('Laravel-FCM');
|
|
$logger->pushHandler(new StreamHandler(storage_path('logs/laravel-fcm.log')));
|
|
|
|
$logMessage = 'notification send to '.count($this->tokens).' devices'.PHP_EOL;
|
|
$logMessage .= 'success: '.$this->numberTokensSuccess.PHP_EOL;
|
|
$logMessage .= 'failures: '.$this->numberTokensFailure.PHP_EOL;
|
|
$logMessage .= 'number of modified token : '.$this->numberTokenModify.PHP_EOL;
|
|
|
|
$logger->info($logMessage);
|
|
}
|
|
|
|
/**
|
|
* Merge two response.
|
|
*
|
|
* @param DownstreamResponse $response
|
|
*/
|
|
public function merge(DownstreamResponse $response)
|
|
{
|
|
$this->numberTokensSuccess += $response->numberSuccess();
|
|
$this->numberTokensFailure += $response->numberFailure();
|
|
$this->numberTokenModify += $response->numberModification();
|
|
|
|
$this->tokensToDelete = array_merge($this->tokensToDelete, $response->tokensToDelete());
|
|
$this->tokensToModify = array_merge($this->tokensToModify, $response->tokensToModify());
|
|
$this->tokensToRetry = array_merge($this->tokensToRetry, $response->tokensToRetry());
|
|
$this->tokensWithError = array_merge($this->tokensWithError, $response->tokensWithError());
|
|
}
|
|
|
|
/**
|
|
* Get the number of device reached with success.
|
|
*
|
|
* @return int
|
|
*/
|
|
public function numberSuccess()
|
|
{
|
|
return $this->numberTokensSuccess;
|
|
}
|
|
|
|
/**
|
|
* Get the number of device which thrown an error.
|
|
*
|
|
* @return int
|
|
*/
|
|
public function numberFailure()
|
|
{
|
|
return $this->numberTokensFailure;
|
|
}
|
|
|
|
/**
|
|
* Get the number of device that you need to modify their token.
|
|
*
|
|
* @return int
|
|
*/
|
|
public function numberModification()
|
|
{
|
|
return $this->numberTokenModify;
|
|
}
|
|
|
|
/**
|
|
* get token to delete.
|
|
*
|
|
* remove all tokens returned by this method in your database
|
|
*
|
|
* @return array
|
|
*/
|
|
public function tokensToDelete()
|
|
{
|
|
return $this->tokensToDelete;
|
|
}
|
|
|
|
/**
|
|
* get token to modify.
|
|
*
|
|
* key: oldToken
|
|
* value: new token
|
|
*
|
|
* find the old token in your database and replace it with the new one
|
|
*
|
|
* @return array
|
|
*/
|
|
public function tokensToModify()
|
|
{
|
|
return $this->tokensToModify;
|
|
}
|
|
|
|
/**
|
|
* Get tokens that you should resend using exponential backoff.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function tokensToRetry()
|
|
{
|
|
return $this->tokensToRetry;
|
|
}
|
|
|
|
/**
|
|
* Get tokens that thrown an error.
|
|
*
|
|
* key : token
|
|
* value : error
|
|
*
|
|
* In production, remove these tokens from you database
|
|
*
|
|
* @return array
|
|
*/
|
|
public function tokensWithError()
|
|
{
|
|
return $this->tokensWithError;
|
|
}
|
|
|
|
/**
|
|
* check if missing tokens was given to the request
|
|
* If true, remove all the empty token in your database.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function hasMissingToken()
|
|
{
|
|
return $this->hasMissingToken;
|
|
}
|
|
}
|