3 namespace Drupal\Core\Batch;
5 use Drupal\Core\Database\Connection;
6 use Drupal\Core\Database\SchemaObjectExistsException;
7 use Symfony\Component\HttpFoundation\Session\SessionInterface;
8 use Drupal\Core\Access\CsrfTokenGenerator;
10 class BatchStorage implements BatchStorageInterface {
15 const TABLE_NAME = 'batch';
18 * The database connection.
20 * @var \Drupal\Core\Database\Connection
22 protected $connection;
27 * @var \Symfony\Component\HttpFoundation\Session\SessionInterface
32 * The CSRF token generator.
34 * @var \Drupal\Core\Access\CsrfTokenGenerator
39 * Constructs the database batch storage service.
41 * @param \Drupal\Core\Database\Connection $connection
42 * The database connection.
43 * @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session
45 * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
46 * The CSRF token generator.
48 public function __construct(Connection $connection, SessionInterface $session, CsrfTokenGenerator $csrf_token) {
49 $this->connection = $connection;
50 $this->session = $session;
51 $this->csrfToken = $csrf_token;
57 public function load($id) {
58 // Ensure that a session is started before using the CSRF token generator.
59 $this->session->start();
61 $batch = $this->connection->query("SELECT batch FROM {batch} WHERE bid = :bid AND token = :token", [
63 ':token' => $this->csrfToken->get($id),
66 catch (\Exception $e) {
67 $this->catchException($e);
71 return unserialize($batch);
79 public function delete($id) {
81 $this->connection->delete('batch')
82 ->condition('bid', $id)
85 catch (\Exception $e) {
86 $this->catchException($e);
93 public function update(array $batch) {
95 $this->connection->update('batch')
96 ->fields(['batch' => serialize($batch)])
97 ->condition('bid', $batch['id'])
100 catch (\Exception $e) {
101 $this->catchException($e);
108 public function cleanup() {
110 // Cleanup the batch table and the queue for failed batches.
111 $this->connection->delete('batch')
112 ->condition('timestamp', REQUEST_TIME - 864000, '<')
115 catch (\Exception $e) {
116 $this->catchException($e);
123 public function create(array $batch) {
124 // Ensure that a session is started before using the CSRF token generator.
125 $this->session->start();
128 // The batch table might not yet exist.
129 $this->doCreate($batch);
131 catch (\Exception $e) {
132 // If there was an exception, try to create the table.
133 if (!$try_again = $this->ensureTableExists()) {
134 // If the exception happened for other reason than the missing table,
135 // propagate the exception.
139 // Now that the table has been created, try again if necessary.
141 $this->doCreate($batch);
148 * @param array $batch
149 * The array representing the batch to create.
151 protected function doCreate(array $batch) {
152 $this->connection->insert('batch')
154 'bid' => $batch['id'],
155 'timestamp' => REQUEST_TIME,
156 'token' => $this->csrfToken->get($batch['id']),
157 'batch' => serialize($batch),
163 * Check if the table exists and create it if not.
165 protected function ensureTableExists() {
167 $database_schema = $this->connection->schema();
168 if (!$database_schema->tableExists(static::TABLE_NAME)) {
169 $schema_definition = $this->schemaDefinition();
170 $database_schema->createTable(static::TABLE_NAME, $schema_definition);
174 // If another process has already created the batch table, attempting to
175 // recreate it will throw an exception. In this case just catch the
176 // exception and do nothing.
177 catch (SchemaObjectExistsException $e) {
184 * Act on an exception when batch might be stale.
186 * If the table does not yet exist, that's fine, but if the table exists and
187 * yet the query failed, then the batch is stale and the exception needs to
195 protected function catchException(\Exception $e) {
196 if ($this->connection->schema()->tableExists(static::TABLE_NAME)) {
202 * Defines the schema for the batch table.
206 public function schemaDefinition() {
208 'description' => 'Stores details about batches (processes that run in multiple HTTP requests).',
211 'description' => 'Primary Key: Unique batch ID.',
212 // This is not a serial column, to allow both progressive and
213 // non-progressive batches. See batch_process().
219 'description' => "A string token generated against the current user's session id and the batch id, used to ensure that only the user who submitted the batch can effectively access it.",
220 'type' => 'varchar_ascii',
225 'description' => 'A Unix timestamp indicating when this batch was submitted for processing. Stale batches are purged at cron time.',
230 'description' => 'A serialized array containing the processing data for the batch.',
236 'primary key' => ['bid'],
238 'token' => ['token'],