Commit 4983036c authored by catch's avatar catch

Issue #2540416 by dawehner, hussainweb: Move update.php back to a front controller

parent 7730866f
<?php
/**
* @file
* Contains \Drupal\Core\Update\UpdateKernel.
*/
namespace Drupal\Core\Update;
use Drupal\Core\DrupalKernel;
use Drupal\Core\Session\AnonymousUserSession;
use Drupal\Core\Site\Settings;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
/**
* Defines a kernel which is used primarily to run the update of Drupal.
*
* We use a dedicated kernel + front controller (update.php) in order to be able
* to repair Drupal if it is in a broken state.
*
* @see update.php
* @see \Drupal\system\Controller\DbUpdateController
*/
class UpdateKernel extends DrupalKernel {
/**
* {@inheritdoc}
*/
public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
try {
static::bootEnvironment();
// First boot up basic things, like loading the include files.
$this->initializeSettings($request);
$this->boot();
$container = $this->getContainer();
/** @var \Symfony\Component\HttpFoundation\RequestStack $request_stack */
$request_stack = $container->get('request_stack');
$request_stack->push($request);
$this->preHandle($request);
// Handle the actual request. We need the session both for authentication
// as well as the DB update, like
// \Drupal\system\Controller\DbUpdateController::batchFinished.
$this->bootSession($request, $type);
$result = $this->handleRaw($request);
$this->shutdownSession($request);
return $result;
}
catch (\Exception $e) {
return $this->handleException($e, $request, $type);
}
}
/**
* Generates the actual result of update.php.
*
* The actual logic of the update is done in the db update controller.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The incoming request.
*
* @return \Symfony\Component\HttpFoundation\Response
* A response object.
*
* @see \Drupal\system\Controller\DbUpdateController
*/
protected function handleRaw(Request $request) {
$container = $this->getContainer();
$this->handleAccess($request, $container);
/** @var \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver */
$controller_resolver = $container->get('controller_resolver');
/** @var callable $db_update_controller */
$db_update_controller = $controller_resolver->getControllerFromDefinition('\Drupal\system\Controller\DbUpdateController::handle');
$this->setupRequestMatch($request);
$arguments = $controller_resolver->getArguments($request, $db_update_controller);
return call_user_func_array($db_update_controller, $arguments);
}
/**
* Boots up the session.
*
* bootSession() + shutdownSession() basically simulates what
* \Drupal\Core\StackMiddleware\Session does.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The incoming request.
*/
protected function bootSession(Request $request) {
$container = $this->getContainer();
/** @var \Symfony\Component\HttpFoundation\Session\SessionInterface $session */
$session = $container->get('session');
$session->start();
$request->setSession($session);
}
/**
* Ensures that the session is saved.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The incoming request.
*/
protected function shutdownSession(Request $request) {
if ($request->hasSession()) {
$request->getSession()->save();
}
}
/**
* Set up the request with fake routing data for update.php.
*
* This fake routing data is needed in order to make batch API work properly.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The incoming request.
*/
protected function setupRequestMatch(Request $request) {
$path = $request->getPathInfo();
$args = explode('/', ltrim($path, '/'));
$request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'system.db_update');
$request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, $this->getContainer()->get('router.route_provider')->getRouteByName('system.db_update'));
$op = $args[0] ?: 'info';
$request->attributes->set('op', $op);
$request->attributes->set('_raw_variables', new ParameterBag(['op' => $op]));
}
/**
* Checks if the current user has rights to access updates page.
*
* If the current user does not have the rights, an exception is thrown.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The incoming request.
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
* Thrown when update.php should not be accessible.
*/
protected function handleAccess(Request $request) {
/** @var \Drupal\Core\Authentication\AuthenticationManager $authentication_manager */
$authentication_manager = $this->getContainer()->get('authentication');
$account = $authentication_manager->authenticate($request) ?: new AnonymousUserSession();
/** @var \Drupal\Core\Session\AccountProxyInterface $current_user */
$current_user = $this->getContainer()->get('current_user');
$current_user->setAccount($account);
/** @var \Drupal\system\Access\DbUpdateAccessCheck $db_update_access */
$db_update_access = $this->getContainer()->get('access_check.db_update');
if (!Settings::get('update_free_access', FALSE) && !$db_update_access->access($account)->isAllowed()) {
throw new AccessDeniedHttpException('In order to run update.php you need to either be logged in as admin or have set $update_free_access in your settings.php.');
}
}
}
...@@ -286,7 +286,7 @@ protected function getAllOptions(\SimpleXMLElement $element) { ...@@ -286,7 +286,7 @@ protected function getAllOptions(\SimpleXMLElement $element) {
* Link position counting from zero. * Link position counting from zero.
* @param string $message * @param string $message
* (optional) A message to display with the assertion. Do not translate * (optional) A message to display with the assertion. Do not translate
* messages: use format_string() to embed variables in the message text, not * messages: use strtr() to embed variables in the message text, not
* t(). If left blank, a default message will be displayed. * t(). If left blank, a default message will be displayed.
* @param string $group * @param string $group
* (optional) The group this message is in, which is displayed in a column * (optional) The group this message is in, which is displayed in a column
...@@ -299,7 +299,7 @@ protected function getAllOptions(\SimpleXMLElement $element) { ...@@ -299,7 +299,7 @@ protected function getAllOptions(\SimpleXMLElement $element) {
*/ */
protected function assertLink($label, $index = 0, $message = '', $group = 'Other') { protected function assertLink($label, $index = 0, $message = '', $group = 'Other') {
$links = $this->xpath('//a[normalize-space(text())=:label]', array(':label' => $label)); $links = $this->xpath('//a[normalize-space(text())=:label]', array(':label' => $label));
$message = ($message ? $message : SafeMarkup::format('Link with label %label found.', array('%label' => $label))); $message = ($message ? $message : strtr('Link with label %label found.', array('%label' => $label)));
return $this->assert(isset($links[$index]), $message, $group); return $this->assert(isset($links[$index]), $message, $group);
} }
...@@ -377,6 +377,30 @@ protected function assertNoLinkByHref($href, $message = '', $group = 'Other') { ...@@ -377,6 +377,30 @@ protected function assertNoLinkByHref($href, $message = '', $group = 'Other') {
return $this->assert(empty($links), $message, $group); return $this->assert(empty($links), $message, $group);
} }
/**
* Passes if a link containing a given href is not found in the main region.
*
* @param string $href
* The full or partial value of the 'href' attribute of the anchor tag.
* @param string $message
* (optional) A message to display with the assertion. Do not translate
* messages: use format_string() to embed variables in the message text, not
* t(). If left blank, a default message will be displayed.
* @param string $group
* (optional) The group this message is in, which is displayed in a column
* in test output. Use 'Debug' to indicate this is debugging output. Do not
* translate this string. Defaults to 'Other'; most tests do not override
* this default.
*
* @return bool
* TRUE if the assertion succeeded, FALSE otherwise.
*/
protected function assertNoLinkByHrefInMainRegion($href, $message = '', $group = 'Other') {
$links = $this->xpath('//main//a[contains(@href, :href)]', array(':href' => $href));
$message = ($message ? $message : SafeMarkup::format('No link containing href %href found.', array('%href' => $href)));
return $this->assert(empty($links), $message, $group);
}
/** /**
* Passes if the raw text IS found on the loaded page, fail otherwise. * Passes if the raw text IS found on the loaded page, fail otherwise.
* *
......
...@@ -160,13 +160,13 @@ public function handle($op, Request $request) { ...@@ -160,13 +160,13 @@ public function handle($op, Request $request) {
$severity = drupal_requirements_severity($requirements); $severity = drupal_requirements_severity($requirements);
if ($severity == REQUIREMENT_ERROR || ($severity == REQUIREMENT_WARNING && empty($_SESSION['update_ignore_warnings']))) { if ($severity == REQUIREMENT_ERROR || ($severity == REQUIREMENT_WARNING && empty($_SESSION['update_ignore_warnings']))) {
$regions['sidebar_first'] = $this->updateTasksList('requirements'); $regions['sidebar_first'] = $this->updateTasksList('requirements');
$output = $this->requirements($severity, $requirements); $output = $this->requirements($severity, $requirements, $request);
} }
else { else {
switch ($op) { switch ($op) {
case 'selection': case 'selection':
$regions['sidebar_first'] = $this->updateTasksList('selection'); $regions['sidebar_first'] = $this->updateTasksList('selection');
$output = $this->selection(); $output = $this->selection($request);
break; break;
case 'run': case 'run':
...@@ -176,12 +176,12 @@ public function handle($op, Request $request) { ...@@ -176,12 +176,12 @@ public function handle($op, Request $request) {
case 'info': case 'info':
$regions['sidebar_first'] = $this->updateTasksList('info'); $regions['sidebar_first'] = $this->updateTasksList('info');
$output = $this->info(); $output = $this->info($request);
break; break;
case 'results': case 'results':
$regions['sidebar_first'] = $this->updateTasksList('results'); $regions['sidebar_first'] = $this->updateTasksList('results');
$output = $this->results(); $output = $this->results($request);
break; break;
// Regular batch ops : defer to batch processing API. // Regular batch ops : defer to batch processing API.
...@@ -204,10 +204,13 @@ public function handle($op, Request $request) { ...@@ -204,10 +204,13 @@ public function handle($op, Request $request) {
/** /**
* Returns the info database update page. * Returns the info database update page.
* *
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*
* @return array * @return array
* A render array. * A render array.
*/ */
protected function info() { protected function info(Request $request) {
// Change query-strings on css/js files to enforce reload for all users. // Change query-strings on css/js files to enforce reload for all users.
_drupal_flush_css_js(); _drupal_flush_css_js();
// Flush the cache of all data for the update status module. // Flush the cache of all data for the update status module.
...@@ -233,12 +236,11 @@ protected function info() { ...@@ -233,12 +236,11 @@ protected function info() {
'#markup' => '<p>' . $this->t('When you have performed the steps above, you may proceed.') . '</p>', '#markup' => '<p>' . $this->t('When you have performed the steps above, you may proceed.') . '</p>',
); );
$url = new Url('system.db_update', array('op' => 'selection'));
$build['link'] = array( $build['link'] = array(
'#type' => 'link', '#type' => 'link',
'#title' => $this->t('Continue'), '#title' => $this->t('Continue'),
'#attributes' => array('class' => array('button', 'button--primary')), '#attributes' => array('class' => array('button', 'button--primary')),
'#url' => $url, '#url' => Url::fromUri($request->getUriForPath('/selection')),
); );
return $build; return $build;
} }
...@@ -246,10 +248,13 @@ protected function info() { ...@@ -246,10 +248,13 @@ protected function info() {
/** /**
* Renders a list of available database updates. * Renders a list of available database updates.
* *
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*
* @return array * @return array
* A render array. * A render array.
*/ */
protected function selection() { protected function selection(Request $request) {
// Make sure there is no stale theme registry. // Make sure there is no stale theme registry.
$this->cache->deleteAll(); $this->cache->deleteAll();
...@@ -342,7 +347,7 @@ protected function selection() { ...@@ -342,7 +347,7 @@ protected function selection() {
unset($build); unset($build);
$build['links'] = array( $build['links'] = array(
'#theme' => 'links', '#theme' => 'links',
'#links' => $this->helpfulLinks(), '#links' => $this->helpfulLinks($request),
); );
// No updates to run, so caches won't get flushed later. Clear them now. // No updates to run, so caches won't get flushed later. Clear them now.
...@@ -364,7 +369,9 @@ protected function selection() { ...@@ -364,7 +369,9 @@ protected function selection() {
else { else {
$build['start']['#title'] = $this->formatPlural($count, '1 pending update', '@count pending updates'); $build['start']['#title'] = $this->formatPlural($count, '1 pending update', '@count pending updates');
} }
$url = new Url('system.db_update', array('op' => 'run')); // @todo Simplify with https://www.drupal.org/node/2548095
$base_url = str_replace('/update.php', '', $request->getBaseUrl());
$url = (new Url('system.db_update', array('op' => 'run')))->setOption('base_url', $base_url);
$build['link'] = array( $build['link'] = array(
'#type' => 'link', '#type' => 'link',
'#title' => $this->t('Apply pending updates'), '#title' => $this->t('Apply pending updates'),
...@@ -380,15 +387,21 @@ protected function selection() { ...@@ -380,15 +387,21 @@ protected function selection() {
/** /**
* Displays results of the update script with any accompanying errors. * Displays results of the update script with any accompanying errors.
* *
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*
* @return array * @return array
* A render array. * A render array.
*/ */
protected function results() { protected function results(Request $request) {
// @todo Simplify with https://www.drupal.org/node/2548095
$base_url = str_replace('/update.php', '', $request->getBaseUrl());
// Report end result. // Report end result.
$dblog_exists = $this->moduleHandler->moduleExists('dblog'); $dblog_exists = $this->moduleHandler->moduleExists('dblog');
if ($dblog_exists && $this->account->hasPermission('access site reports')) { if ($dblog_exists && $this->account->hasPermission('access site reports')) {
$log_message = $this->t('All errors have been <a href="@url">logged</a>.', array( $log_message = $this->t('All errors have been <a href="@url">logged</a>.', array(
'@url' => Url::fromRoute('dblog.overview')->toString(TRUE)->getGeneratedUrl(), '@url' => Url::fromRoute('dblog.overview')->setOption('base_url', $base_url)->toString(TRUE)->getGeneratedUrl(),
)); ));
} }
else { else {
...@@ -396,7 +409,7 @@ protected function results() { ...@@ -396,7 +409,7 @@ protected function results() {
} }
if (!empty($_SESSION['update_success'])) { if (!empty($_SESSION['update_success'])) {
$message = '<p>' . $this->t('Updates were attempted. If you see no failures below, you may proceed happily back to your <a href="@url">site</a>. Otherwise, you may need to update your database manually.', array('@url' => Url::fromRoute('<front>')->toString(TRUE)->getGeneratedUrl())) . ' ' . $log_message . '</p>'; $message = '<p>' . $this->t('Updates were attempted. If you see no failures below, you may proceed happily back to your <a href="@url">site</a>. Otherwise, you may need to update your database manually.', array('@url' => Url::fromRoute('<front>')->setOption('base_url', $base_url)->toString(TRUE)->getGeneratedUrl())) . ' ' . $log_message . '</p>';
} }
else { else {
$last = reset($_SESSION['updates_remaining']); $last = reset($_SESSION['updates_remaining']);
...@@ -420,7 +433,7 @@ protected function results() { ...@@ -420,7 +433,7 @@ protected function results() {
); );
$build['links'] = array( $build['links'] = array(
'#theme' => 'links', '#theme' => 'links',
'#links' => $this->helpfulLinks(), '#links' => $this->helpfulLinks($request),
); );
// Output a list of info messages. // Output a list of info messages.
...@@ -492,12 +505,15 @@ protected function results() { ...@@ -492,12 +505,15 @@ protected function results() {
/** /**
* Renders a list of requirement errors or warnings. * Renders a list of requirement errors or warnings.
* *
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*
* @return array * @return array
* A render array. * A render array.
*/ */
public function requirements($severity, array $requirements) { public function requirements($severity, array $requirements, Request $request) {
$options = $severity == REQUIREMENT_WARNING ? array('continue' => 1) : array(); $options = $severity == REQUIREMENT_WARNING ? array('continue' => 1) : array();
$try_again_url = Url::fromRoute('system.db_update', $options)->toString(TRUE)->getGeneratedUrl(); $try_again_url = Url::fromUri($request->getUriForPath(''))->setOptions(['query' => $options])->toString(TRUE)->getGeneratedUrl();
$build['status_report'] = array( $build['status_report'] = array(
'#theme' => 'status_report', '#theme' => 'status_report',
...@@ -603,7 +619,7 @@ protected function triggerBatch(Request $request) { ...@@ -603,7 +619,7 @@ protected function triggerBatch(Request $request) {
); );
batch_set($batch); batch_set($batch);
return batch_process('update.php/results', Url::fromRoute('system.db_update', array('op' => 'start'))); return batch_process(Url::fromUri($request->getUriForPath('/results')), Url::fromUri($request->getUriForPath('/start')));
} }
/** /**
...@@ -640,18 +656,23 @@ public static function batchFinished($success, $results, $operations) { ...@@ -640,18 +656,23 @@ public static function batchFinished($success, $results, $operations) {
/** /**
* Provides links to the homepage and administration pages. * Provides links to the homepage and administration pages.
* *
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*
* @return array * @return array
* An array of links. * An array of links.
*/ */
protected function helpfulLinks() { protected function helpfulLinks(Request $request) {
// @todo Simplify with https://www.drupal.org/node/2548095
$base_url = str_replace('/update.php', '', $request->getBaseUrl());
$links['front'] = array( $links['front'] = array(
'title' => $this->t('Front page'), 'title' => $this->t('Front page'),
'url' => Url::fromRoute('<front>'), 'url' => Url::fromRoute('<front>')->setOption('base_url', $base_url),
); );
if ($this->account->hasPermission('access administration pages')) { if ($this->account->hasPermission('access administration pages')) {
$links['admin-pages'] = array( $links['admin-pages'] = array(
'title' => $this->t('Administration pages'), 'title' => $this->t('Administration pages'),
'url' => Url::fromRoute('system.admin'), 'url' => Url::fromRoute('system.admin')->setOption('base_url', $base_url),
); );
} }
return $links; return $links;
......
<?php
/**
* @file
* Contains \Drupal\system\Tests\Update\UpdatePathWithBrokenRoutingTest.
*/
namespace Drupal\system\Tests\Update;
/**
* Tests the update path with a broken router.
*
* @group Update
*/
class UpdatePathWithBrokenRoutingTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../tests/fixtures/update/drupal-8.bare.standard.php.gz',
__DIR__ . '/../../../tests/fixtures/update/drupal-8.broken_routing.php',
];
parent::setUp();
}
/**
* Tests running update.php with some form of broken routing.
*/
public function testWithBrokenRouting() {
// Make sure we can get to the front page.
$this->drupalGet('<front>');
$this->assertResponse(200);
// Simulate a broken router, and make sure the front page is
// inaccessible.
\Drupal::state()->set('update_script_test_broken_inbound', TRUE);
\Drupal::service('cache_tags.invalidator')->invalidateTags(['route_match', 'rendered']);
$this->drupalGet('<front>');
$this->assertResponse(500);
// The exceptions are expected. Do not interpret them as a test failure.
// Not using File API; a potential error must trigger a PHP warning.
unlink(\Drupal::root() . '/' . $this->siteDirectory . '/error.log');
foreach ($this->assertions as $key => $assertion) {
if (strpos($assertion['message'], 'core/modules/system/tests/modules/update_script_test/src/PathProcessor/BrokenInboundPathProcessor.php') !== FALSE) {
unset($this->assertions[$key]);
$this->deleteAssert($assertion['message_id']);
}
}
$this->runUpdates();
// Remove the simulation of the broken router, and make sure we can get to
// the front page again.
\Drupal::state()->set('update_script_test_broken_inbound', FALSE);
$this->drupalGet('<front>');
$this->assertResponse(200);
}
}
...@@ -153,7 +153,7 @@ function testNoUpdateFunctionality() { ...@@ -153,7 +153,7 @@ function testNoUpdateFunctionality() {
$this->clickLink(t('Continue')); $this->clickLink(t('Continue'));
$this->assertText(t('No pending updates.')); $this->assertText(t('No pending updates.'));
$this->assertNoLink('Administration pages'); $this->assertNoLink('Administration pages');
$this->assertNoLinkByHref('update.php', 0); $this->assertNoLinkByHrefInMainRegion('update.php', 0);
$this->clickLink('Front page'); $this->clickLink('Front page');
$this->assertResponse(200); $this->assertResponse(200);
...@@ -164,7 +164,7 @@ function testNoUpdateFunctionality() { ...@@ -164,7 +164,7 @@ function testNoUpdateFunctionality() {
$this->clickLink(t('Continue')); $this->clickLink(t('Continue'));
$this->assertText(t('No pending updates.')); $this->assertText(t('No pending updates.'));
$this->assertLink('Administration pages'); $this->assertLink('Administration pages');
$this->assertNoLinkByHref('update.php', 1); $this->assertNoLinkByHrefInMainRegion('update.php', 1);
$this->clickLink('Administration pages'); $this->clickLink('Administration pages');
$this->assertResponse(200); $this->assertResponse(200);
} }
...@@ -198,7 +198,7 @@ function testSuccessfulUpdateFunctionality() { ...@@ -198,7 +198,7 @@ function testSuccessfulUpdateFunctionality() {
$this->assertText('Updates were attempted.'); $this->assertText('Updates were attempted.');
$this->assertLink('logged'); $this->assertLink('logged');
$this->assertLink('Administration pages'); $this->assertLink('Administration pages');
$this->assertNoLinkByHref('update.php', 1); $this->assertNoLinkByHrefInMainRegion('update.php', 1);
$this->clickLink('Administration pages'); $this->clickLink('Administration pages');
$this->assertResponse(200); $this->assertResponse(200);
} }
...@@ -253,7 +253,7 @@ protected function updateScriptTest($maintenance_mode) { ...@@ -253,7 +253,7 @@ protected function updateScriptTest($maintenance_mode) {
// Verify that there are no links to different parts of the workflow. // Verify that there are no links to different parts of the workflow.
$this->assertNoLink('Administration pages'); $this->assertNoLink('Administration pages');
$this->assertNoLinkByHref('update.php', 0); $this->assertNoLinkByHrefInMainRegion('update.php', 0);
$this->assertNoLink('logged'); $this->assertNoLink('logged');
// Verify the front page can be visited following the upgrade. // Verify the front page can be visited following the upgrade.
......
...@@ -448,16 +448,14 @@ system.batch_page.json: ...@@ -448,16 +448,14 @@ system.batch_page.json:
options: options:
_admin_route: TRUE _admin_route: TRUE
# Note: This route just exists for generating URLs, the dedicated
# frontcontroller is used if the URL is accessed.
system.db_update: system.db_update:
path: '/update.php/{op}' path: '/update.php/{op}'
defaults: defaults:
_title: 'Drupal database update'
_controller: '\Drupal\system\Controller\DbUpdateController::handle'
op: 'info' op: 'info'
options:
_maintenance_access: TRUE
requirements: requirements:
_access_system_update: 'TRUE' _access: 'TRUE'
system.admin_content: system.admin_content:
path: '/admin/content' path: '/admin/content'
......
<?php
use Drupal\Core\Database\Database;
$connection = Database::getConnection();
$config = unserialize($connection->query("SELECT data FROM {config} where name = :name", [':name' => 'core.extension'])->fetchField());
$config['module']['update_script_test'] = 0;
$connection->update('config')
->fields(['data' => serialize($config)])
->condition('name', 'core.extension')
->execute();
$connection->insert('key_value')
->fields(['collection' => 'system.schema', 'name' => 'update_script_test', 'value' => serialize(8000)])
->execute();
<?php
/**
* @file
* Contains \Drupal\update_script_test\PathProcessor\BrokenInboundPathProcessor.
*/
namespace Drupal\update_script_test\PathProcessor;
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Drupal\Core\State\StateInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Example path processor which breaks on inbound.
*/
class BrokenInboundPathProcessor implements InboundPathProcessorInterface {
/**
* The state.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* Constructs a new BrokenInboundPathProcessor instance.
*
* @param \Drupal\Core\State\StateInterface $state
* The state.
*/
public function __construct(StateInterface $state) {
$this->state = $state;
}
/**
* {@inheritdoc}