3 namespace Drupal\Core\Update;
5 use Drupal\Core\DrupalKernel;
6 use Drupal\Core\Session\AnonymousUserSession;
7 use Drupal\Core\Site\Settings;
8 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
9 use Symfony\Component\HttpFoundation\ParameterBag;
10 use Symfony\Component\HttpFoundation\Request;
11 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
14 * Defines a kernel which is used primarily to run the update of Drupal.
16 * We use a dedicated kernel + front controller (update.php) in order to be able
17 * to repair Drupal if it is in a broken state.
20 * @see \Drupal\system\Controller\DbUpdateController
22 class UpdateKernel extends DrupalKernel {
27 public function discoverServiceProviders() {
28 parent::discoverServiceProviders();
30 $this->serviceProviderClasses['app']['update_kernel'] = 'Drupal\Core\Update\UpdateServiceProvider';
36 protected function initializeContainer() {
37 // Always force a container rebuild, in order to be able to override some
38 // services, see \Drupal\Core\Update\UpdateServiceProvider.
39 $this->containerNeedsRebuild = TRUE;
40 $container = parent::initializeContainer();
47 protected function cacheDrupalContainer(array $container_definition) {
48 // Don't save this particular container to cache, so it does not leak into
49 // the main site at all.
56 public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
58 static::bootEnvironment();
60 // First boot up basic things, like loading the include files.
61 $this->initializeSettings($request);
63 $container = $this->getContainer();
64 /** @var \Symfony\Component\HttpFoundation\RequestStack $request_stack */
65 $request_stack = $container->get('request_stack');
66 $request_stack->push($request);
67 $this->preHandle($request);
69 // Handle the actual request. We need the session both for authentication
70 // as well as the DB update, like
71 // \Drupal\system\Controller\DbUpdateController::batchFinished.
72 $this->bootSession($request, $type);
73 $result = $this->handleRaw($request);
74 $this->shutdownSession($request);
78 catch (\Exception $e) {
79 return $this->handleException($e, $request, $type);
84 * Generates the actual result of update.php.
86 * The actual logic of the update is done in the db update controller.
88 * @param \Symfony\Component\HttpFoundation\Request $request
89 * The incoming request.
91 * @return \Symfony\Component\HttpFoundation\Response
94 * @see \Drupal\system\Controller\DbUpdateController
96 protected function handleRaw(Request $request) {
97 $container = $this->getContainer();
99 $this->handleAccess($request, $container);
101 /** @var \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver */
102 $controller_resolver = $container->get('controller_resolver');
104 /** @var callable $db_update_controller */
105 $db_update_controller = $controller_resolver->getControllerFromDefinition('\Drupal\system\Controller\DbUpdateController::handle');
107 $this->setupRequestMatch($request);
109 $arguments = $controller_resolver->getArguments($request, $db_update_controller);
110 return call_user_func_array($db_update_controller, $arguments);
114 * Boots up the session.
116 * bootSession() + shutdownSession() basically simulates what
117 * \Drupal\Core\StackMiddleware\Session does.
119 * @param \Symfony\Component\HttpFoundation\Request $request
120 * The incoming request.
122 protected function bootSession(Request $request) {
123 $container = $this->getContainer();
124 /** @var \Symfony\Component\HttpFoundation\Session\SessionInterface $session */
125 $session = $container->get('session');
127 $request->setSession($session);
131 * Ensures that the session is saved.
133 * @param \Symfony\Component\HttpFoundation\Request $request
134 * The incoming request.
136 protected function shutdownSession(Request $request) {
137 if ($request->hasSession()) {
138 $request->getSession()->save();
143 * Set up the request with fake routing data for update.php.
145 * This fake routing data is needed in order to make batch API work properly.
147 * @param \Symfony\Component\HttpFoundation\Request $request
148 * The incoming request.
150 protected function setupRequestMatch(Request $request) {
151 $path = $request->getPathInfo();
152 $args = explode('/', ltrim($path, '/'));
154 $request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'system.db_update');
155 $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, $this->getContainer()->get('router.route_provider')->getRouteByName('system.db_update'));
156 $op = $args[0] ?: 'info';
157 $request->attributes->set('op', $op);
158 $request->attributes->set('_raw_variables', new ParameterBag(['op' => $op]));
162 * Checks if the current user has rights to access updates page.
164 * If the current user does not have the rights, an exception is thrown.
166 * @param \Symfony\Component\HttpFoundation\Request $request
167 * The incoming request.
169 * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
170 * Thrown when update.php should not be accessible.
172 protected function handleAccess(Request $request) {
173 /** @var \Drupal\Core\Authentication\AuthenticationManager $authentication_manager */
174 $authentication_manager = $this->getContainer()->get('authentication');
175 $account = $authentication_manager->authenticate($request) ?: new AnonymousUserSession();
177 /** @var \Drupal\Core\Session\AccountProxyInterface $current_user */
178 $current_user = $this->getContainer()->get('current_user');
179 $current_user->setAccount($account);
181 /** @var \Drupal\system\Access\DbUpdateAccessCheck $db_update_access */
182 $db_update_access = $this->getContainer()->get('access_check.db_update');
184 if (!Settings::get('update_free_access', FALSE) && !$db_update_access->access($account)->isAllowed()) {
185 throw new AccessDeniedHttpException('In order to run update.php you need to either be logged in as admin or have set $settings[\'update_free_access\'] in your settings.php.');