You are here

class SmsMessageProcessor in SMS Framework 2.x

Same name and namespace in other branches
  1. 8 src/EventSubscriber/SmsMessageProcessor.php \Drupal\sms\EventSubscriber\SmsMessageProcessor
  2. 2.1.x src/EventSubscriber/SmsMessageProcessor.php \Drupal\sms\EventSubscriber\SmsMessageProcessor

Handles messages before they are processed by queue(), send(), or incoming().

Messages queued via queue() are destined for send() or incoming(), they will not be double processed.

Hierarchy

  • class \Drupal\sms\EventSubscriber\SmsMessageProcessor implements \Symfony\Component\EventDispatcher\EventSubscriberInterface

Expanded class hierarchy of SmsMessageProcessor

1 string reference to 'SmsMessageProcessor'
sms.services.yml in ./sms.services.yml
sms.services.yml
1 service uses SmsMessageProcessor
sms.sms_message_processor in ./sms.services.yml
Drupal\sms\EventSubscriber\SmsMessageProcessor

File

src/EventSubscriber/SmsMessageProcessor.php, line 31

Namespace

Drupal\sms\EventSubscriber
View source
class SmsMessageProcessor implements EventSubscriberInterface {

  /**
   * The event dispatcher.
   *
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
   */
  protected $eventDispatcher;

  /**
   * The configuration factory.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactory;

  /**
   * Creates a new SmsMessageProcessor controller.
   *
   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
   *   The event dispatcher.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The configuration factory.
   */
  public function __construct(EventDispatcherInterface $event_dispatcher, ConfigFactoryInterface $config_factory) {
    $this->eventDispatcher = $event_dispatcher;
    $this->configFactory = $config_factory;
  }

  /**
   * Ensures gateway supports incoming messages.
   *
   * @param \Drupal\sms\Event\SmsMessageEvent $event
   *   An SMS message process event.
   */
  public function ensureIncomingSupport(SmsMessageEvent $event) {
    $sms_messages = $event
      ->getMessages();
    foreach ($sms_messages as $sms_message) {
      if ($sms_message
        ->getDirection() == Direction::INCOMING) {
        $gateway = $sms_message
          ->getGateway();
        if (!$gateway instanceof SmsGatewayInterface) {
          throw new SmsException('Gateway not set on incoming message');
        }
        if (!$gateway
          ->supportsIncoming()) {
          throw new SmsException(sprintf('Gateway `%s` does not support incoming messages.', $gateway
            ->id()));
        }
      }
    }
  }

  /**
   * Ensures there is a result, and reports for each recipient.
   *
   * @param \Drupal\sms\Event\SmsMessageEvent $event
   *   An SMS message process event.
   */
  public function ensureReportsPreprocess(SmsMessageEvent $event) {
    $sms_messages = $event
      ->getMessages();
    foreach ($sms_messages as $sms_message) {

      // Event can be for any direction. Capture incoming only for preprocess.
      if ($sms_message
        ->getDirection() == Direction::INCOMING) {
        $this
          ->ensureReports($sms_message);
      }
    }
  }

  /**
   * Ensures there is a result, and reports for each recipient.
   *
   * @param \Drupal\sms\Event\SmsMessageEvent $event
   *   An SMS message process event.
   */
  public function ensureReportsPostprocess(SmsMessageEvent $event) {
    $sms_messages = $event
      ->getMessages();
    foreach ($sms_messages as $sms_message) {
      $this
        ->ensureReports($sms_message);
    }
  }

  /**
   * Ensures there is a result, and reports for each recipient.
   *
   * @param \Drupal\sms\Message\SmsMessageInterface $sms_message
   *   A message to validate.
   *
   * @throws \Drupal\sms\Exception\SmsPluginReportException
   *   Thrown if result or reports are invalid.
   */
  protected function ensureReports(SmsMessageInterface $sms_message) {
    $result = $sms_message
      ->getResult();
    if (!$result instanceof SmsMessageResultInterface) {
      throw new SmsPluginReportException('Missing result for message.');
    }
    $message_recipients = $sms_message
      ->getRecipients();
    $result_recipients = array_map(function (SmsDeliveryReportInterface $report) {
      return $report
        ->getRecipient();
    }, $result
      ->getReports());
    $difference_count = count(array_diff($message_recipients, $result_recipients));
    if ($difference_count) {
      throw new SmsPluginReportException(sprintf('Missing reports for %s recipient(s).', $difference_count));
    }
  }

  /**
   * Ensures there is at least one recipient on the message.
   *
   * @param \Drupal\sms\Event\SmsMessageEvent $event
   *   The SMS message preprocess event.
   */
  public function ensureRecipients(SmsMessageEvent $event) {
    $sms_messages = $event
      ->getMessages();
    foreach ($sms_messages as $sms_message) {
      if ($sms_message
        ->getDirection() == Direction::OUTGOING) {
        $recipients = $sms_message
          ->getRecipients();
        if (!count($recipients)) {
          throw new RecipientRouteException(sprintf('There are no recipients.'));
        }
      }
    }
  }

  /**
   * Ensure all recipients are routed to a gateway.
   *
   * Messages will be split into multiple if recipients need to be routed to
   * different gateways.
   *
   * @param \Drupal\sms\Event\SmsMessageEvent $event
   *   The SMS message preprocess event.
   *
   * @throws \Drupal\sms\Exception\RecipientRouteException
   *   Guarantees a gateway is set on the message, otherwise this exception is
   *   thrown.
   */
  public function ensureGateways(SmsMessageEvent $event) {
    $sms_messages = $event
      ->getMessages();
    $result = [];

    // Ignore messages if they already have a gateway.
    foreach ($sms_messages as $k => $sms_message) {
      if ($sms_message
        ->getGateway() instanceof SmsGatewayInterface) {
        unset($sms_messages[$k]);
        $result[] = $sms_message;
      }
    }

    // Ensure all recipients in this message can be routed to a gateway.
    foreach ($sms_messages as $sms_message) {
      $gateways = [];
      $recipients_all = $sms_message
        ->getRecipients();
      foreach ($recipients_all as $recipient) {
        $gateway = $this
          ->getGatewayForPhoneNumber($recipient);
        if ($gateway instanceof SmsGatewayInterface) {
          $gateways[$gateway
            ->id()][] = $recipient;
        }
        else {
          $event
            ->stopPropagation();
          throw new RecipientRouteException(sprintf('Unable to determine gateway for recipient %s.', $recipient));
        }
      }

      // Recreate SMS messages depending on the gateway.
      $base = $sms_message instanceof EntityInterface ? $sms_message
        ->createDuplicate() : clone $sms_message;
      $base
        ->removeRecipients($recipients_all);
      foreach ($gateways as $gateway_id => $recipients) {
        $new = $base instanceof EntityInterface ? $base
          ->createDuplicate() : clone $base;
        $result[] = $new
          ->addRecipients($recipients)
          ->setGateway(SmsGateway::load($gateway_id));
      }
    }
    $event
      ->setMessages($result);
  }

  /**
   * Get a gateway for a phone number.
   *
   * @param string $recipient
   *   A recipient phone number.
   *
   * @return \Drupal\sms\Entity\SmsGatewayInterface|null
   *   A gateway for the phone number, or NULL if there is no gateway.
   */
  protected function getGatewayForPhoneNumber($recipient) {
    $event = new RecipientGatewayEvent($recipient);

    /** @var \Drupal\sms\Event\RecipientGatewayEvent $event */
    $event = $this->eventDispatcher
      ->dispatch(SmsEvents::MESSAGE_GATEWAY, $event);
    $gateways = $event
      ->getGatewaysSorted();

    // Use the gateway with the greatest weight.
    $gateway = array_shift($gateways);
    if ($gateway instanceof SmsGatewayInterface) {
      return $gateway;
    }

    // If no gateways found for a phone number, use site fallback default if
    // available.
    $gateway_id = $this->configFactory
      ->get('sms.settings')
      ->get('fallback_gateway');
    return isset($gateway_id) ? SmsGateway::load($gateway_id) : NULL;
  }

  /**
   * Add a delivery report URL to messages.
   *
   * @param \Drupal\sms\Event\SmsMessageEvent $event
   *   The SMS message preprocess event.
   */
  public function deliveryReportUrl(SmsMessageEvent $event) {
    foreach ($event
      ->getMessages() as &$sms_message) {
      if (!$sms_message
        ->getOption('delivery_report_url')) {
        $url = $sms_message
          ->getGateway()
          ->getPushReportUrl();
        try {
          $url = $url
            ->setAbsolute()
            ->toString();
          $sms_message
            ->setOption('delivery_report_url', $url);
        } catch (RouteNotFoundException $e) {
        }
      }
    }
  }

  /**
   * Split messages to overcome gateway limits.
   *
   * @param \Drupal\sms\Event\SmsMessageEvent $event
   *   The SMS message preprocess event.
   */
  public function chunkMaxRecipients(SmsMessageEvent $event) {
    $result = [];
    foreach ($event
      ->getMessages() as $sms_message) {
      if ($sms_message
        ->getDirection() == Direction::OUTGOING) {
        $max = $sms_message
          ->getGateway()
          ->getMaxRecipientsOutgoing();
        $result = array_merge($result, $sms_message
          ->chunkByRecipients($max));
      }
      else {
        $result[] = $sms_message;
      }
    }
    $event
      ->setMessages($result);
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    $events[SmsEvents::MESSAGE_PRE_PROCESS][] = [
      'ensureIncomingSupport',
      1024,
    ];

    // Ensure reports for incoming messages.
    $events[SmsEvents::MESSAGE_PRE_PROCESS][] = [
      'ensureReportsPreprocess',
      1024,
    ];
    $events[SmsEvents::MESSAGE_PRE_PROCESS][] = [
      'ensureRecipients',
      1024,
    ];
    $events[SmsEvents::MESSAGE_PRE_PROCESS][] = [
      'ensureGateways',
      1024,
    ];
    $events[SmsEvents::MESSAGE_PRE_PROCESS][] = [
      'deliveryReportUrl',
    ];
    $events[SmsEvents::MESSAGE_PRE_PROCESS][] = [
      'chunkMaxRecipients',
      -1024,
    ];

    // Ensure reports for outgoing messages.
    $events[SmsEvents::MESSAGE_OUTGOING_POST_PROCESS][] = [
      'ensureReportsPostprocess',
      1024,
    ];
    return $events;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
SmsMessageProcessor::$configFactory protected property The configuration factory.
SmsMessageProcessor::$eventDispatcher protected property The event dispatcher.
SmsMessageProcessor::chunkMaxRecipients public function Split messages to overcome gateway limits.
SmsMessageProcessor::deliveryReportUrl public function Add a delivery report URL to messages.
SmsMessageProcessor::ensureGateways public function Ensure all recipients are routed to a gateway.
SmsMessageProcessor::ensureIncomingSupport public function Ensures gateway supports incoming messages.
SmsMessageProcessor::ensureRecipients public function Ensures there is at least one recipient on the message.
SmsMessageProcessor::ensureReports protected function Ensures there is a result, and reports for each recipient.
SmsMessageProcessor::ensureReportsPostprocess public function Ensures there is a result, and reports for each recipient.
SmsMessageProcessor::ensureReportsPreprocess public function Ensures there is a result, and reports for each recipient.
SmsMessageProcessor::getGatewayForPhoneNumber protected function Get a gateway for a phone number.
SmsMessageProcessor::getSubscribedEvents public static function
SmsMessageProcessor::__construct public function Creates a new SmsMessageProcessor controller.