Simplify exception handling for lazy-load pattern

Created on 8 May 2016, about 8 years ago
Updated 6 November 2023, 8 months ago

There are several places in core that use this pattern to implement lazy loading (this snippet from core/lib/Drupal/Core/Batch/BatchStorage.php):

  public function create(array $batch) {
    // Ensure that a session is started before using the CSRF token generator.
    $this->session->start();
    $try_again = FALSE;
    try {
      // The batch table might not yet exist.
      $this->doCreate($batch);
    }
    catch (\Exception $e) {
      // If there was an exception, try to create the table.
      if (!$try_again = $this->ensureTableExists()) {
        // If the exception happened for other reason than the missing table,
        // propagate the exception.
        throw $e;
      }
    }
    // Now that the table has been created, try again if necessary.
    if ($try_again) {
      $this->doCreate($batch);
    }
  }

I think that is a little more complicated than necessary, and I think it is worth cleaning this up, since exception handling should be as transparent as possible. I would like to replace the above code with this, eliminating the $try_again variable:

  public function create(array $batch) {
    // Ensure that a session is started before using the CSRF token generator.
    $this->session->start();
    try {
      // The batch table might not yet exist.
      $this->doCreate($batch);
    }
    catch (\Exception $e) {
      // If there was an exception, then try to create the table.
      if ($this->ensureTableExists()) {
        // Now that the table has been created, try again.
        $this->doCreate($batch);
      }
      else {
        // If the exception happened for other reason than the missing table,
        // then propagate the exception.
        throw $e;
      }
    }
  }

I first noticed this pattern when reviewing #2664322: key_value table is only used by a core service but it depends on system install → , but @dawehner pointed out this this pattern is already used elsewhere in core. In that issue, the pattern is slightly different: the foo() method does the work itself, and calls itself from the catch block, instead of having a separate method doFoo() that does the actual work.

I believe that the code is functionally identical, so this should not make any API changes.

📌 Task
Status

Needs work

Version

11.0 🔥

Component
Base  →

Last updated about 3 hours ago

Created by

🇺🇸United States benjifisher Boston area

Live updates comments and jobs are added and updated live.
  • Needs reroll

    The patch will have to be re-rolled with new suggestions/changes described in the comments in the issue.

Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

Production build 0.69.0 2024