View source  
  <?php
namespace Drupal\push_notifications;
class PushNotificationsBroadcasterGcm implements PushNotificationsBroadcasterInterface {
  
  const PUSH_NOTIFICATIONS_GCM_SERVER_POST_URL = 'https://android.googleapis.com/gcm/send';
  
  protected $tokens;
  
  protected $payload;
  
  protected $countAttempted = 0;
  
  protected $countSuccess = 0;
  
  protected $success = FALSE;
  
  protected $message;
  
  private $tokenBundles;
  
  public function __construct() {
  }
  
  public function setTokens($tokens) {
    $this->tokens = $tokens;
  }
  
  public function setMessage($message) {
    $this->message = $message;
    
    $this->payload = array(
      'alert' => $message,
    );
  }
  
  public function sendBroadcast() {
    if (empty($this->tokens) || empty($this->payload)) {
      throw new \Exception('No tokens or payload set.');
    }
    
    $this->tokenBundles = ceil(count($this->tokens) / 1000);
    
    $this->countAttempted = count($this->tokens);
    
    for ($i = 0; $i < $this->tokenBundles; $i++) {
      try {
        $bundledTokens = array_slice($this->tokens, $i * 1000, 1000, FALSE);
        $result = $this
          ->sendTokenBundle($bundledTokens);
        $this
          ->processResult($result, $bundledTokens);
      } catch (\Exception $e) {
        \Drupal::logger('push_notifications')
          ->error($e
          ->getMessage());
      }
    }
    
    $this->success = TRUE;
  }
  
  public function getResults() {
    return array(
      'network' => PUSH_NOTIFICATIONS_NETWORK_ID_ANDROID,
      'payload' => $this->payload,
      'count_attempted' => $this->countAttempted,
      'count_success' => $this->countSuccess,
      'success' => $this->success,
    );
  }
  
  private function sendTokenBundle($tokens) {
    
    $data = array();
    foreach ($this->payload as $key => $value) {
      if ($key != 'alert') {
        $data['data'][$key] = $value;
      }
    }
    
    $data['registration_ids'] = $tokens;
    $data['collapse_key'] = (string) time();
    $data['data']['message'] = $this->message;
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, self::PUSH_NOTIFICATIONS_GCM_SERVER_POST_URL);
    curl_setopt($curl, CURLOPT_HTTPHEADER, $this
      ->getHeaders());
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($curl, CURLOPT_POST, TRUE);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
    curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
    $response_raw = curl_exec($curl);
    $info = curl_getinfo($curl);
    curl_close($curl);
    $response = FALSE;
    if (isset($response_raw)) {
      $response = json_decode($response_raw);
    }
    return array(
      'info' => $info,
      'response' => $response,
      'response_raw' => $response_raw,
    );
  }
  
  private function processResult($result, $tokens) {
    
    if ($result['info']['http_code'] != 200) {
      throw new \Exception('Connection could not be authorized with Google Play. Check your API key.');
    }
    
    if ($result['info']['http_code'] == 200 && !empty($result['response']->failure)) {
      \Drupal::logger('push_notifications')
        ->notice("Google's Server returned an error: @response_raw", array(
        '@response_raw' => $result['response_raw'],
      ));
      
      foreach ($result['response']->results as $token_index => $message_result) {
        if (!empty($message_result->error)) {
          
          if ($message_result->error == 'NotRegistered' || $message_result->error == 'InvalidRegistration') {
            $entity_type = 'push_notifications_token';
            $query = \Drupal::entityQuery($entity_type)
              ->condition('token', $tokens[$token_index]);
            $entity_ids = $query
              ->execute();
            $entityTypeManager = \Drupal::entityTypeManager()
              ->getStorage($entity_type);
            $entity = $entityTypeManager
              ->load(array_shift($entity_ids));
            $entity
              ->delete();
            \Drupal::logger('push_notifications')
              ->notice("GCM token not valid anymore. Removing token @token", array(
              '@$token' => $tokens[$token_index],
            ));
          }
        }
      }
    }
    
    if ($result['info']['http_code'] == 200 && !empty($result['response']->success)) {
      $this->countSuccess += $result['response']->success;
    }
  }
  
  private function getHeaders() {
    $headers = array();
    $headers[] = 'Content-Type:application/json';
    $headers[] = 'Authorization:key=' . \Drupal::config('push_notifications.gcm')
      ->get('api_key');
    return $headers;
  }
}