Method _batch_current_set is called without include core/includes/batch.inc

Created on 23 September 2019, almost 5 years ago
Updated 26 February 2023, over 1 year ago

Problem/Motivation

I have installed the module simple access log to log any request made to drupal. This module intercepts the event kernel.request via event subscriber to extract request information to save it on the database.

However when a batch process is launched, the request is broken because this event subscriber tries to get the title of the batch page calling \Drupal::service('title_resolver')->getTitle($request, $route), which on turn calls Drupal\system\Controller\BatchController::batchPageTitle(), and this method try to call the function _batch_current_set, but the file core/includes/batch.inc that contains this function is not loaded yet, causing the next error:

The website encountered an unexpected error. Please try again later.
Error: Call to undefined function Drupal\system\Controller\_batch_current_set() in Drupal\system\Controller\BatchController->batchPageTitle() (line 92 of core/modules/system/src/Controller/BatchController.php).
Drupal\system\Controller\BatchController->batchPageTitle()
call_user_func_array(Array, Array) (Line: 58)
Drupal\Core\Controller\TitleResolver->getTitle(Object, Object) (Line: 82)
Drupal\simple_access_log\Controller\SimpleAccessLog->logValues() (Line: 25)
Drupal\simple_access_log\EventSubscriber\SimpleAccessLogSubscriber->executeLogFunction(Object, β€˜kernel.request’, Object)
call_user_func(Array, Object, β€˜kernel.request’, Object) (Line: 111)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(β€˜kernel.request’, Object) (Line: 127)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 68)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 67)
Drupal\simple_oauth\HttpMiddleware\BasicAuthSwap->handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 106)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 85)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 49)
Asm89\Stack\Cors->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 52)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 693)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)

Everything seems to indicate that it is assumed that this file is already loaded when before executing this method.

Steps to reproduce

To reproduce this bug with Drupal core alone, enable the block module, place the breadcrumb block for your theme, and visit the /batch/anything path, where anything is any string.

Proposed resolution

Include the file required before calling _batch_current_set() as the method batchPage does, or check if the function exists before calling it.

πŸ› Bug report
Status

Fixed

Version

10.1 ✨

Component
BatchΒ  β†’

Last updated 4 days ago

Created by

πŸ‡¨πŸ‡΄Colombia dogamboar

Live updates comments and jobs are added and updated live.
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.

  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    This issue is being reviewed by the kind folks in Slack, #needs-review-queue-initiative. We are working to keep the size of Needs Review queue [2700+ issues] to around 400 (1 month or less), following Review a patch or merge request β†’ as a guide.

    Re-triggering for 10.1 and 9.5.x tests but don't expect failures

    Followed the steps in the IS and can confirm the issue
    Patch does fix the issue

  • Status changed to Needs work over 1 year ago
  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    The test being added here is kinda of incorrect. Batches are never requested at /batch/something - they are requested at /batch?id=12

    Also the fix for the breadcrumbs is different than for the bug reported in the issue summary. We're never really going to have a breadcrumb for /batch so we should be exlcuding it like we do /user. I.e in \Drupal\system\PathBasedBreadcrumbBuilder::build() we could do something like:

        // /user is just a redirect, so skip it.
        // @todo Find a better way to deal with /user.
        $exclude['/user'] = TRUE;
        $exclude['/batch'] = TRUE;
    

    But I think this issue points to a need to add a route option to disable the breadcrumb - that way contrib could make a similar fix. This probably should be split out into a separate fix.

    So thinking about the original issue. The problem is you're not going to log the correct title with the current fix because in order to do that you need to load the batch. I think we need to do something like:

    • Load core/includes/batch.inc in the constructor
    • Change the batchPageTitle() to get batch and load it from storage if necessary.

    I.e

      public function batchPageTitle(Request $request) {
        $batch = &batch_get();
    
        if (!($request_id = $request->query->get('id'))) {
          return '';
        }
    
        // Retrieve the current state of the batch.
        if (!$batch) {
          $batch = \Drupal::service('batch.storage')->load($request_id);
        }
    
        if (!$batch) {
          return '';
        }
    
        $current_set = _batch_current_set();
        return !empty($current_set['title']) ? $current_set['title'] : '';
      }
    
  • Status changed to Needs review over 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States mfb San Francisco

    Ok let's see if that passes tests.

  • Status changed to Needs work over 1 year ago
  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ
    +++ b/core/modules/system/tests/src/Functional/Batch/PageTest.php
    @@ -67,6 +67,11 @@ public function testBatchProgressPageTitle() {
    +    // Test that the batch system emits a 404 page when appropriate.
    +    $this->drupalPlaceBlock('system_breadcrumb_block');
    +    $this->drupalGet('batch/foobar');
    +    $this->assertSession()->pageTextContains('Page not found');
    

    We should change this test. We should be getting the title for route like /batch?id=1234124123 ... we'll have to create a request and route match object to pass to the title resolver - see how \Drupal\system\PathBasedBreadcrumbBuilder::build() does it.

  • Status changed to Needs review over 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States mfb San Francisco

    is it enough to just call the broken method in a test, like this?

    If not, then yeah the test could call \Drupal::service('title_resolver')->getTitle($request, $route) as per the original issue summary.

    For the record, I actually hit this bug as per the test I originally wrote - via an automated pentesting bot hitting random bogus paths including batch/[some string] - which tried and failed to build the breadcrumb.

  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    @mfb so we need to fix the path breadcrumb builder issue in a separate related issue because even with this fix the breadcrumbs on batch/some_nonsense is incorrect. I think the test in #20 is okay - I think what'd be really nice is a unit test that tests getting the batch from storage (using a mock). Also we need to inject the batch storage service into the class.

  • Status changed to Needs work over 1 year ago
  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    THis part of #21

    I think what'd be really nice is a unit test that tests getting the batch from storage (using a mock). Also we need to inject the batch storage service into the class.

    Is something we should do on this issue. We also need to open another issue about excluding paths from the breadcrumb builder.

  • Status changed to Needs review over 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States mfb San Francisco

    Did a bit more work on this - now injecting the service, and changed test from kernel test to unit test. But for the latter to work, I had to require form.inc, otherwise function batch_get() does not exist.

  • Status changed to Needs work over 1 year ago
  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ
    +++ b/core/modules/system/src/Controller/BatchController.php
    --- /dev/null
    +++ b/core/modules/system/tests/src/Unit/Batch/BatchControllerTest.php
    
    +++ b/core/modules/system/tests/src/Unit/Batch/BatchControllerTest.php
    @@ -0,0 +1,30 @@
    +  /**
    +   * Tests title callback.
    +   *
    +   * @covers ::batchPageTitle
    +   */
    +  public function testBatchPageTitle() {
    +    $controller = new BatchController($this->root, $this->createMock(BatchStorageInterface::class));
    +    require_once $this->root . '/core/includes/form.inc';
    +    $this->assertSame('', $controller->batchPageTitle(new Request()));
    +  }
    

    Nice. So we should also test that a title is returned when batch_get() returns something. TO do this we can mock the load method on the batch storage.

    Also as we're loading code and changing the state of the system let's add the @runTestsInSeparateProcesses annotation to the test class.

  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    I opened the related issue - πŸ› PathBasedBreadcrumbBuilder needs to be able to exclude pointless paths Needs work

  • Status changed to Needs review over 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States mfb San Francisco

    This seems to work as a minimal mock batch array

  • Status changed to RTBC over 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Seems #25 have been addressed in
    + $this->assertSame('foobar', $controller->batchPageTitle(new Request(['id' => 1234])));

    This looks good. Adding 3344200 to my list also.

  • Status changed to Needs work over 1 year ago
  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ
    +++ b/core/modules/system/tests/src/Unit/Batch/BatchControllerTest.php
    @@ -0,0 +1,34 @@
    +  /**
    +   * Tests title callback.
    +   *
    +   * @covers ::batchPageTitle
    +   */
    +  public function testBatchPageTitle() {
    +    $batch_storage = $this->createMock(BatchStorageInterface::class);
    +    $controller = new BatchController($this->root, $batch_storage);
    +    require_once $this->root . '/core/includes/form.inc';
    +    $this->assertSame('', $controller->batchPageTitle(new Request()));
    +    $batch = ['sets' => [['title' => 'foobar']], 'current_set' => 0];
    +    $batch_storage->method('load')->willReturn($batch);
    +    $this->assertSame('foobar', $controller->batchPageTitle(new Request(['id' => 1234])));
    +  }
    

    This test should cover both situations - where the batch exists and where it does not.

  • Status changed to Needs review over 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States mfb San Francisco

    Added another test case in there too - batch could be returned by &batch_get()

  • Status changed to RTBC over 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Going to try it again. If it's wrong I do apologize!

  • Status changed to Fixed over 1 year ago
  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    Given the constructor changes I think we can only commit this fix to 10.1.x - I considered asking for the deprecation dance and allow a NULL for batch storage but there's nothing in contrib that extends this class so I think we fine.

    Committed f1dbd84 and pushed to 10.1.x. Thanks!

    diff --git a/core/modules/system/tests/src/Unit/Batch/BatchControllerTest.php b/core/modules/system/tests/src/Unit/Batch/BatchControllerTest.php
    index 172e2feae9..e68649fd37 100644
    --- a/core/modules/system/tests/src/Unit/Batch/BatchControllerTest.php
    +++ b/core/modules/system/tests/src/Unit/Batch/BatchControllerTest.php
    @@ -32,7 +32,9 @@ public function testBatchPageTitle() {
         $this->assertSame('', $controller->batchPageTitle(new Request(['id' => 1234])));
         $this->assertSame('foobar', $controller->batchPageTitle(new Request(['id' => 1234])));
         // Test batch returned by &batch_get() call.
    -    $this->assertSame('foobar', $controller->batchPageTitle(new Request(['id' => 1234])));
    +    $batch = &batch_get();
    +    $batch['sets']['0']['title'] = 'Updated title';
    +    $this->assertSame('Updated title', $controller->batchPageTitle(new Request(['id' => 1234])));
       }
     
     }
    

    Slightly improved the test on commit.

  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.69.0 2024