Batch.php 5.2 KB
Newer Older
Pawel G's avatar
Pawel G committed
1
2
<?php

3
namespace Drupal\simple_sitemap\Batch;
Pawel G's avatar
Pawel G committed
4

5
use Drupal\Core\StringTranslation\StringTranslationTrait;
Pawel G's avatar
Pawel G committed
6
use Drupal\Core\Cache\Cache;
Pawel G's avatar
Pawel G committed
7

Pawel G's avatar
Pawel G committed
8
/**
Pawel G's avatar
Pawel G committed
9
10
 * Class Batch
 * @package Drupal\simple_sitemap\Batch
Pawel G's avatar
Pawel G committed
11
 *
Pawel G's avatar
Pawel G committed
12
13
 * The services of this class are not injected, as this class looses its state
 * on every method call because of how the batch APi works.
Pawel G's avatar
Pawel G committed
14
 */
Pawel G's avatar
Pawel G committed
15
class Batch {
16

17
  use StringTranslationTrait;
18

Pawel G's avatar
Pawel G committed
19
20
21
  /**
   * @var array
   */
22
  protected $batch;
Pawel G's avatar
Pawel G committed
23
24
25
26

  /**
   * @var array
   */
27
  protected $batchInfo;
Pawel G's avatar
Pawel G committed
28

Pawel G's avatar
Pawel G committed
29
  const BATCH_TITLE = 'Generating XML sitemap';
30
31
32
  const BATCH_INIT_MESSAGE = 'Initializing batch...';
  const BATCH_ERROR_MESSAGE = 'An error has occurred. This may result in an incomplete XML sitemap.';
  const BATCH_PROGRESS_MESSAGE = 'Processing @current out of @total link types.';
Pawel G's avatar
Pawel G committed
33
34
  const REGENERATION_FINISHED_MESSAGE = "The <a href='@url' target='_blank'>XML sitemap</a> has been regenerated for all languages.";
  const REGENERATION_FINISHED_ERROR_MESSAGE = 'The sitemap generation finished with an error.';
Pawel G's avatar
Pawel G committed
35

36
37
38
  /**
   * Batch constructor.
   */
39
  public function __construct() {
Pawel G's avatar
Pawel G committed
40
    $this->batch = [
Pawel G's avatar
Pawel G committed
41
      'title' => $this->t(self::BATCH_TITLE),
42
43
44
      'init_message' => $this->t(self::BATCH_INIT_MESSAGE),
      'error_message' => $this->t(self::BATCH_ERROR_MESSAGE),
      'progress_message' => $this->t(self::BATCH_PROGRESS_MESSAGE),
Pawel G's avatar
Pawel G committed
45
      'operations' => [],
Pawel G's avatar
Pawel G committed
46
      'finished' => [__CLASS__, 'finishGeneration'],
Pawel G's avatar
Pawel G committed
47
    ];
48
49
  }

50
  /**
Pawel G's avatar
Pawel G committed
51
   * @param array $batch_info
52
   */
Pawel G's avatar
Pawel G committed
53
  public function setBatchInfo(array $batch_info) {
54
    $this->batchInfo = $batch_info;
Pawel G's avatar
Pawel G committed
55
56
  }

57
58
59
  /**
   * Starts the batch process depending on where it was requested from.
   */
Pawel G's avatar
Pawel G committed
60
  public function start() {
61
    switch ($this->batchInfo['from']) {
62

Pawel G's avatar
Pawel G committed
63
      case 'form':
64
        // Start batch process.
65
        batch_set($this->batch);
Pawel G's avatar
Pawel G committed
66
        break;
67

Pawel G's avatar
Pawel G committed
68
      case 'drush':
69
        // Start drush batch process.
70
        batch_set($this->batch);
Pawel G's avatar
Pawel G committed
71
72

        // See https://www.drupal.org/node/638712
Pawel G's avatar
Pawel G committed
73
        $this->batch =& batch_get();
Pawel G's avatar
Pawel G committed
74
        $this->batch['progressive'] = FALSE;
Pawel G's avatar
Pawel G committed
75

Pawel G's avatar
Pawel G committed
76
        drush_log($this->t(self::BATCH_INIT_MESSAGE), 'status');
Pawel G's avatar
Pawel G committed
77
78
        drush_backend_batch_process();
        break;
79

80
      case 'backend':
81
        // Start backend batch process.
82
        batch_set($this->batch);
Pawel G's avatar
Pawel G committed
83
84

        // See https://www.drupal.org/node/638712
Pawel G's avatar
Pawel G committed
85
        $this->batch =& batch_get();
86
        $this->batch['progressive'] = FALSE;
Pawel G's avatar
Pawel G committed
87

88
        // todo: Does not take advantage of batch API and eventually runs out of memory on very large sites. Use queue API instead?
Pawel G's avatar
Pawel G committed
89
        batch_process();
90
        break;
91
92

      case 'nobatch':
93
94
        // Call each batch operation the way the Drupal batch API would do, but
        // within one process (so in fact not using batch API here, just
Pawel G's avatar
Pawel G committed
95
        // mimicking it to avoid code duplication).
Pawel G's avatar
Pawel G committed
96
        $context = [];
Pawel G's avatar
Pawel G committed
97
        foreach ($this->batch['operations'] as $i => $operation) {
98
99
100
          $operation[1][] = &$context;
          call_user_func_array($operation[0], $operation[1]);
        }
Pawel G's avatar
Pawel G committed
101
        $this->finishGeneration(TRUE, $context['results'], []);
102
        break;
Pawel G's avatar
Pawel G committed
103
104
105
    }
  }

106
  /**
Pawel G's avatar
Pawel G committed
107
   * Adds an operation to the batch.
108
   *
109
   * @param string $processing_service
Pawel G's avatar
Pawel G committed
110
   * @param array $data
111
   */
112
  public function addOperation($processing_service, array $data) {
Pawel G's avatar
Pawel G committed
113
    $this->batch['operations'][] = [
114
      __CLASS__ . '::generate', [$processing_service, $data, $this->batchInfo],
Pawel G's avatar
Pawel G committed
115
    ];
Pawel G's avatar
Pawel G committed
116
117
118
  }

  /**
119
   * Batch callback function which generates urls to entity paths.
Pawel G's avatar
Pawel G committed
120
   *
121
   * @param array $entity_info
122
123
   * @param array $batch_info
   * @param array &$context
Pawel G's avatar
Pawel G committed
124
125
   *
   * @see https://api.drupal.org/api/drupal/core!includes!form.inc/group/batch/8
Pawel G's avatar
Pawel G committed
126
127
   */

Pawel G's avatar
Pawel G committed
128
  /**
129
   * Batch callback function which generates URLs.
Pawel G's avatar
Pawel G committed
130
   *
131
132
   * @param $processing_service
   * @param array $data
133
   * @param array $batch_info
134
   * @param $context
135
136
   *
   * @see https://api.drupal.org/api/drupal/core!includes!form.inc/group/batch/8
Pawel G's avatar
Pawel G committed
137
   */
138
139
  public static function generate($processing_service, array $data, array $batch_info, &$context) {
    \Drupal::service($processing_service)
140
141
      ->setContext($context)
      ->setBatchInfo($batch_info)
142
      ->generate($data);
143
144
145
146
147
148
149
150
  }

  /**
   * Callback function called by the batch API when all operations are finished.
   *
   * @param $success
   * @param $results
   * @param $operations
151
152
   *
   * @see https://api.drupal.org/api/drupal/core!includes!form.inc/group/batch/8
153
154
   */
  public static function finishGeneration($success, $results, $operations) {
Pawel G's avatar
Pawel G committed
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
    if ($success) {
      $remove_sitemap = empty($results['chunk_count']);
      if (!empty($results['generate']) || $remove_sitemap) {
        \Drupal::service('simple_sitemap.sitemap_generator')
          ->generateSitemap($results['generate'], $remove_sitemap);
      }
      Cache::invalidateTags(['simple_sitemap']);
      \Drupal::service('simple_sitemap.logger')->m(self::REGENERATION_FINISHED_MESSAGE,
        ['@url' => $GLOBALS['base_url'] . '/sitemap.xml'])
//        ['@url' => $this->sitemapGenerator->getCustomBaseUrl() . '/sitemap.xml']) //todo: Use actual base URL for message.
        ->display('status')
        ->log('info');
    }
    else {
      \Drupal::service('simple_sitemap.logger')->m(self::REGENERATION_FINISHED_ERROR_MESSAGE)
        ->display('error', 'administer sitemap settings')
        ->log('error');
    }
Pawel G's avatar
Pawel G committed
173
  }
Pawel G's avatar
Pawel G committed
174

Pawel G's avatar
Pawel G committed
175
}