diff --git a/core/core.services.yml b/core/core.services.yml index 40c6a8a..e5ad735 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -1048,7 +1048,7 @@ services: - { name: event_subscriber } main_content_renderer.html: class: Drupal\Core\Render\MainContent\HtmlRenderer - arguments: ['@title_resolver', '@plugin.manager.display_variant', '@event_dispatcher', '@module_handler', '@renderer', '@render_cache', '%renderer.config%'] + arguments: ['@title_resolver', '@plugin.manager.display_variant', '@event_dispatcher', '@module_handler', '@renderer', '@render_cache', '%renderer.config%', '@shutdown_registry'] tags: - { name: render.main_content_renderer, format: html } main_content_renderer.ajax: @@ -1088,7 +1088,7 @@ services: - { name: event_subscriber } bare_html_page_renderer: class: Drupal\Core\Render\BareHtmlPageRenderer - arguments: ['@renderer', '@html_response.attachments_processor'] + arguments: ['@renderer', '@html_response.attachments_processor', '@shutdown_registry'] lazy: true private_key: class: Drupal\Core\PrivateKey @@ -1657,3 +1657,5 @@ services: class: Drupal\Core\EventSubscriber\RssResponseRelativeUrlFilter tags: - { name: event_subscriber } + shutdown_registry: + class: Drupal\Core\Utility\ShutdownRegistry diff --git a/core/includes/batch.inc b/core/includes/batch.inc index 84291ae..61d22b4 100644 --- a/core/includes/batch.inc +++ b/core/includes/batch.inc @@ -48,7 +48,7 @@ function _batch_page(Request $request) { } // Register database update for the end of processing. - drupal_register_shutdown_function('_batch_shutdown'); + \Drupal::service('shutdown_registry')->registerShutdownCallable('_batch_shutdown'); $build = array(); diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 9c976eb..49de811 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -974,31 +974,30 @@ function drupal_placeholder($text) { * * @see register_shutdown_function() * @ingroup php_wrappers + * @deprecated in Drupal 8.4.0, will be removed before Drupal 9.0.0. + * Use \Drupal\Core\Utility\ShutdownRegistry::registerShutdownCallable() to + * register functions and + * \Drupal\Core\Utility\ShutdownRegistry::getCallbacks() to retrieve + * registered functions. */ function &drupal_register_shutdown_function($callback = NULL) { - // We cannot use drupal_static() here because the static cache is reset during - // batch processing, which breaks batch handling. - static $callbacks = array(); + /** @var \Drupal\Core\Utility\ShutdownRegistry $shutdown_registry */ + $shutdown_registry = \Drupal::service('shutdown_registry'); if (isset($callback)) { - // Only register the internal shutdown function once. - if (empty($callbacks)) { - register_shutdown_function('_drupal_shutdown_function'); - } $args = func_get_args(); // Remove $callback from the arguments. unset($args[0]); - // Save callback and arguments - $callbacks[] = array('callback' => $callback, 'arguments' => $args); + $shutdown_registry->registerShutdownCallable($callback, $args); } - return $callbacks; + return $shutdown_registry->getCallbacks(); } /** * Executes registered shutdown functions. */ function _drupal_shutdown_function() { - $callbacks = &drupal_register_shutdown_function(); + $callbacks = Drupal::service('shutdown_registry')->getCallbacks(); // Set the CWD to DRUPAL_ROOT as it is not guaranteed to be the same as it // was in the normal context of execution. diff --git a/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php b/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php index 6c73a0d..0f07c37 100644 --- a/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php +++ b/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php @@ -2,6 +2,8 @@ namespace Drupal\Core\Render; +use Drupal\Core\Utility\ShutdownRegistry; + /** * Default bare HTML page renderer. */ @@ -22,16 +24,26 @@ class BareHtmlPageRenderer implements BareHtmlPageRendererInterface { protected $htmlResponseAttachmentsProcessor; /** + * The shutdown registry service. + * + * @var \Drupal\Core\Utility\ShutdownRegistry + */ + protected $shutdownRegistry; + + /** * Constructs a new BareHtmlPageRenderer. * * @param \Drupal\Core\Render\RendererInterface $renderer * The renderer service. * @param \Drupal\Core\Render\AttachmentsResponseProcessorInterface $html_response_attachments_processor * The HTML response attachments processor service. + * @param \Drupal\Core\Utility\ShutdownRegistry $shutdown_registry + * The shutdown registry. */ - public function __construct(RendererInterface $renderer, AttachmentsResponseProcessorInterface $html_response_attachments_processor) { + public function __construct(RendererInterface $renderer, AttachmentsResponseProcessorInterface $html_response_attachments_processor, ShutdownRegistry $shutdown_registry) { $this->renderer = $renderer; $this->htmlResponseAttachmentsProcessor = $html_response_attachments_processor; + $this->shutdownRegistry = $shutdown_registry; } /** @@ -65,7 +77,7 @@ public function renderBarePage(array $content, $title, $page_theme_property, arr system_page_attachments($html['page']); $this->renderer->renderRoot($html); - $response = new HtmlResponse(); + $response = new HtmlResponse('', 200, [], $this->shutdownRegistry->preventEarlyFlush()); $response->setContent($html); // Process attachments, because this does not go via the regular render // pipeline, but will be sent directly. diff --git a/core/lib/Drupal/Core/Render/HtmlResponse.php b/core/lib/Drupal/Core/Render/HtmlResponse.php index 555e504..1241cfa 100644 --- a/core/lib/Drupal/Core/Render/HtmlResponse.php +++ b/core/lib/Drupal/Core/Render/HtmlResponse.php @@ -5,7 +5,6 @@ use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Cache\CacheableResponseInterface; use Drupal\Core\Cache\CacheableResponseTrait; -use Symfony\Component\HttpFoundation\Response; /** * A response that contains and can expose cacheability metadata and attachments. diff --git a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php index 54c40f6..735b9e3 100644 --- a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php +++ b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php @@ -15,6 +15,7 @@ use Drupal\Core\Render\RendererInterface; use Drupal\Core\Render\RenderEvents; use Drupal\Core\Routing\RouteMatchInterface; +use Drupal\Core\Utility\ShutdownRegistry; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Request; @@ -81,6 +82,13 @@ class HtmlRenderer implements MainContentRendererInterface { protected $rendererConfig; /** + * The shutdown registry service. + * + * @var \Drupal\Core\Utility\ShutdownRegistry + */ + protected $shutdownRegistry; + + /** * Constructs a new HtmlRenderer. * * @param \Drupal\Core\Controller\TitleResolverInterface $title_resolver @@ -97,8 +105,10 @@ class HtmlRenderer implements MainContentRendererInterface { * The render cache service. * @param array $renderer_config * The renderer configuration array. + * @param \Drupal\Core\Utility\ShutdownRegistry $shutdown_registry + * The shutdown registry. */ - public function __construct(TitleResolverInterface $title_resolver, PluginManagerInterface $display_variant_manager, EventDispatcherInterface $event_dispatcher, ModuleHandlerInterface $module_handler, RendererInterface $renderer, RenderCacheInterface $render_cache, array $renderer_config) { + public function __construct(TitleResolverInterface $title_resolver, PluginManagerInterface $display_variant_manager, EventDispatcherInterface $event_dispatcher, ModuleHandlerInterface $module_handler, RendererInterface $renderer, RenderCacheInterface $render_cache, array $renderer_config, ShutdownRegistry $shutdown_registry) { $this->titleResolver = $title_resolver; $this->displayVariantManager = $display_variant_manager; $this->eventDispatcher = $event_dispatcher; @@ -106,6 +116,7 @@ public function __construct(TitleResolverInterface $title_resolver, PluginManage $this->renderer = $renderer; $this->renderCache = $render_cache; $this->rendererConfig = $renderer_config; + $this->shutdownRegistry = $shutdown_registry; } /** @@ -161,9 +172,14 @@ public function renderResponse(array $main_content, Request $request, RouteMatch // entire render cache, regardless of the cache bin. $content['#cache']['tags'][] = 'rendered'; - $response = new HtmlResponse($content, 200, [ - 'Content-Type' => 'text/html; charset=UTF-8', - ]); + $response = new HtmlResponse( + $content, + 200, + [ + 'Content-Type' => 'text/html; charset=UTF-8', + ], + $this->shutdownRegistry->preventEarlyFlush() + ); return $response; } diff --git a/core/lib/Drupal/Core/Render/Response.php b/core/lib/Drupal/Core/Render/Response.php new file mode 100644 index 0000000..6013e67 --- /dev/null +++ b/core/lib/Drupal/Core/Render/Response.php @@ -0,0 +1,55 @@ +preventEarlyFlush = $prevent_early_flush; + } + + /** + * {@inheritdoc} + */ + public function send() { + $this->sendHeaders(); + $this->sendContent(); + + if (!$this->preventEarlyFlush && function_exists('fastcgi_finish_request')) { + fastcgi_finish_request(); + } + elseif ('cli' !== PHP_SAPI) { + static::closeOutputBuffers(0, TRUE); + } + return $this; + } + +} diff --git a/core/lib/Drupal/Core/Utility/ShutdownRegistry.php b/core/lib/Drupal/Core/Utility/ShutdownRegistry.php new file mode 100644 index 0000000..d8c001c --- /dev/null +++ b/core/lib/Drupal/Core/Utility/ShutdownRegistry.php @@ -0,0 +1,73 @@ +callbacks)) { + register_shutdown_function('_drupal_shutdown_function'); + } + // Save callback and arguments. + $this->callbacks[] = ['callback' => $callable, 'arguments' => $arguments]; + if ($prevent_early_flush) { + $this->preventEarlyFlush = TRUE; + } + } + + /** + * Gets whether early flushing of response should be prevented. + * + * @return bool + * TRUE if early flushing should be prevented, FALSE otherwise. + */ + public function preventEarlyFlush() { + return $this->preventEarlyFlush; + } + + /** + * Get shutdown callbacks. + * + * @return \callable[] + * The callbacks. + */ + public function &getCallbacks() { + return $this->callbacks; + } + +}