You are here

public function Mailer::sendSpool in Simplenews 8.2

Same name and namespace in other branches
  1. 8 src/Mail/Mailer.php \Drupal\simplenews\Mail\Mailer::sendSpool()
  2. 3.x src/Mail/Mailer.php \Drupal\simplenews\Mail\Mailer::sendSpool()

Send simplenews newsletters from the spool.

Individual newsletter emails are stored in database spool. Sending is triggered by cron or immediately when the node is saved. Mail data is retrieved from the spool, rendered and send one by one If sending is successful the message is marked as send in the spool.

@todo: Redesign API to allow language counter in multilingual sends.

Parameters

int $limit: (Optional) The maximum number of mails to send. Defaults to unlimited.

array $conditions: (Optional) Array of spool conditions which are applied to the query.

Return value

int Returns the amount of sent mails.

Overrides MailerInterface::sendSpool

1 call to Mailer::sendSpool()
Mailer::attemptImmediateSend in src/Mail/Mailer.php
Send mail spool immediately if cron should not be used.

File

src/Mail/Mailer.php, line 223

Class

Mailer
Default Mailer.

Namespace

Drupal\simplenews\Mail

Code

public function sendSpool($limit = SpoolStorageInterface::UNLIMITED, array $conditions = []) {
  $check_counter = 0;

  // Send pending messages from database cache.
  $spool = $this->spoolStorage
    ->getMails($limit, $conditions);
  if (count($spool) > 0) {

    // Switch to the anonymous user.
    $anonymous_user = new AnonymousUserSession();
    $this->accountSwitcher
      ->switchTo($anonymous_user);
    $count_fail = $count_skipped = $count_success = 0;
    $sent = [];
    $this
      ->startTimer();
    try {
      while ($mail = $spool
        ->nextMail()) {
        $mail
          ->setKey('node');
        $result = $this
          ->sendMail($mail);
        $spool
          ->setLastMailResult($result);

        // Check every n emails if we exceed the limit.
        // When PHP maximum execution time is almost elapsed we interrupt
        // sending. The remainder will be sent during the next cron run.
        if (++$check_counter >= static::SEND_CHECK_INTERVAL && ini_get('max_execution_time') > 0) {
          $check_counter = 0;

          // Stop sending if a percentage of max execution time was exceeded.
          $elapsed = $this
            ->getCurrentExecutionTime();
          if ($elapsed > static::SEND_TIME_LIMIT * ini_get('max_execution_time')) {
            $this->logger
              ->warning('Sending interrupted: PHP maximum execution time almost exceeded. Remaining newsletters will be sent during the next cron run. If this warning occurs regularly you should reduce the !cron_throttle_setting.', [
              '!cron_throttle_setting' => Link::fromTextAndUrl($this
                ->t('Cron throttle setting'), Url::fromRoute('simplenews.settings_mail')),
            ]);
            break;
          }
        }
      }
    } catch (AbortSendingException $e) {
      $this->logger
        ->error($e
        ->getMessage());
    }

    // Calculate counts.
    $results_table = [];
    $freq = array_fill(0, SpoolStorageInterface::STATUS_FAILED + 1, 0);
    foreach ($spool
      ->getResults() as $row) {
      $freq[$row->result]++;
      if (isset(static::TRACK_RESULTS[$row->result])) {
        $item =& $results_table[$row->entity_type][$row->entity_id][$row->langcode][$row->result];
        $item = ($item ?? 0) + 1;
      }
    }

    // Update subscriber count.
    if ($this->lock
      ->acquire('simplenews_update_sent_count')) {
      foreach ($results_table as $entity_type => $ids) {
        $storage = $this->entityTypeManager
          ->getStorage($entity_type);
        foreach ($ids as $entity_id => $languages) {
          $storage
            ->resetCache([
            $entity_id,
          ]);
          $entity = $storage
            ->load($entity_id);
          foreach ($languages as $langcode => $counts) {
            $translation = $entity
              ->getTranslation($langcode);
            $translation->simplenews_issue->sent_count += $counts[SpoolStorageInterface::STATUS_DONE] ?? 0;
            $translation->simplenews_issue->error_count += $counts[SpoolStorageInterface::STATUS_FAILED] ?? 0;
          }
          $entity
            ->save();
        }
      }
      $this->lock
        ->release('simplenews_update_sent_count');
    }

    // Report sent result and elapsed time. On Windows systems getrusage() is
    // not implemented and hence no elapsed time is available.
    $log_array = [
      '%success' => $freq[SpoolStorageInterface::STATUS_DONE],
      '%skipped' => $freq[SpoolStorageInterface::STATUS_SKIPPED],
      '%fail' => $freq[SpoolStorageInterface::STATUS_FAILED],
      '%retry' => $freq[SpoolStorageInterface::STATUS_PENDING],
    ];
    if (function_exists('getrusage')) {
      $log_array['%sec'] = round($this
        ->getCurrentExecutionTime(), 1);
      $this->logger
        ->notice('%success emails sent in %sec seconds, %skipped skipped, %fail failed permanently, %retry failed retrying.', $log_array);
    }
    else {
      $this->logger
        ->notice('%success emails sent, %skipped skipped, %fail failed permanently, %retry failed retrying.', $log_array);
    }
    $this->state
      ->set('simplenews.last_cron', REQUEST_TIME);
    $this->state
      ->set('simplenews.last_sent', $freq[SpoolStorageInterface::STATUS_DONE]);
    $this->accountSwitcher
      ->switchBack();
    return $freq[SpoolStorageInterface::STATUS_DONE];
  }
}