Commit 3afa92d4 authored by willzyx's avatar willzyx

Issue #2448391 by willzyx: devel_silent() doesn't work properly

parent c3810218
......@@ -234,29 +234,45 @@ function devel_set_handler($handlers) {
/**
* Checks whether Devel may be active.
*
* Devel is automatically disabled when:
* - The request come from a php cli.
* - The request has 'XDEBUG_PROFILE' parameter set.
* - The request come from Apache Benchmark.
* - The currently executing script is the front controller update.php.
* - The returned response isn't a \Drupal\Core\Render\HtmlResponse instance.
*
* You can manually disable devel at runtime by setting the global variable
* 'devel_shutdown' to FALSE in your code:
*
* @code
* if ($some_condition) {
* $GLOBALS['devel_shutdown'] = FALSE;
* }
* @endcode
*
* You can manually disable devel adding the '_devel_silent' requirement
* to your route definitions:
*
* @code
* mymodule.silent_route:
* path: '/my-path'
* defaults:
* _controller: '\Drupal\mymodule\Controller\MyController::method'
* requirements:
* _devel_silent: 'TRUE'
* @endcode
*
* @return bool
* Return whether Devel may be active.
*/
function devel_silent() {
$route_name = \Drupal::routeMatch()->getRouteName();
$excluded_routes = array(
'file.ajax_progress',
'file.ajax_upload',
'image.style_private',
'system.batch_page.html',
'system.batch_page.json',
'system.files',
'system.private_file_download',
);
return
(PHP_SAPI === 'cli') ||
(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'ApacheBench') !== FALSE) ||
!empty($_REQUEST['XDEBUG_PROFILE']) ||
isset($GLOBALS['devel_shutdown']) ||
strstr($_SERVER['PHP_SELF'], 'update.php') ||
in_array($route_name, $excluded_routes);
(($route = \Drupal::routeMatch()->getRouteObject()) && $route->getRequirement('_devel_silent'));
}
/**
......@@ -444,19 +460,6 @@ function devel_shutdown_real() {
// of the Content-type http headers tested below (e.g. text/xml,
// text/javascript, etc). This is is advised where applicable.
if (!devel_silent() && !isset($GLOBALS['devel_shutdown']) && !isset($GLOBALS['devel_redirecting'])) {
// Try not to break non html pages.
if (function_exists('drupal_get_http_header')) {
$header = drupal_get_http_header('content-type');
if ($header) {
$formats = array('xml', 'javascript', 'json', 'plain', 'image',
'application', 'csv', 'x-comma-separated-values');
foreach ($formats as $format) {
if (strstr($header, $format)) {
return;
}
}
}
}
if (\Drupal::currentUser()->hasPermission('access devel information')) {
$queries = (devel_query_enabled() ? Database::getLog('devel', 'default') : NULL);
......
......@@ -11,6 +11,7 @@ use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Database\Database;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Page\DefaultHtmlPageRenderer;
use Drupal\Core\Render\HtmlResponse;
use Drupal\Core\Routing\UrlGeneratorInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
......@@ -154,6 +155,10 @@ class DevelEventSubscriber implements EventSubscriberInterface {
$GLOBALS['devel_redirecting'] = TRUE;
}
}
// Enable Devel only on html pages.
elseif(!$response instanceof HtmlResponse) {
$GLOBALS['devel_shutdown'] = FALSE;
}
}
/**
......
<?php
/**
* @file
* Contains \Drupal\devel\Tests\DevelSilentTest.
*/
namespace Drupal\devel\Tests;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Url;
use Drupal\file\Entity\File;
use Drupal\image\Entity\ImageStyle;
use Drupal\simpletest\WebTestBase;
/**
* Tests devel silent.
*
* @group devel
*/
class DevelSilentTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['devel', 'devel_test', 'image', 'file'];
/**
* Tests devel silent.
*/
public function testDevelSilent() {
// Enable timer so we can test if devel_silent() works properly by checking
// the output of the page.
\Drupal::configFactory()->getEditable('devel.settings')->set('timer', TRUE)->save();
$web_user = $this->drupalCreateUser([
'administer site configuration',
'access devel information',
'administer software updates'
]);
$this->drupalLogin($web_user);
$this->drupalGet('');
$this->assertText('Page execution time was');
// Ensure that devel is disabled if the request has XDEBUG_PROFILE
// parameter set. Get the user profile page so we are sure that we are not
// redirected and we don't lose the query string parameter.
$this->drupalGet('user/' . $web_user->id(), ['query' => ['XDEBUG_PROFILE' => '1']]);
$this->assertNoText('Page execution time was');
// TODO this assertion seems to break testbot, we need to investigate.
// Ensure that devel is disabled if the request come from Apache Benchmark.
// $this->drupalGet('', [], ['User-Agent: ApacheBench/1.0']);
// $this->assertNoText('Page execution time was');
// Ensure that devel is disabled on the front controller update.php.
$this->drupalGet(Url::fromRoute('system.db_update'));
$this->assertResponse(200);
$this->assertNoText('Page execution time was');
// Ensure that devel is disabled if $GLOBALS['devel_shutdown'] is set
// somewhere in the code.
$this->drupalGet('devel-silent/global-shoutdown');
$this->assertText(t('$GLOBALS[\'devel_shutdown\'] = FALSE forces devel to be inactive.'));
$this->assertNoText('Page execution time was');
// Ensure that devel is disabled if response come from routes that are
// declared with '_devel_silent' requirement.
$this->drupalGet('devel-silent/route-requirement');
$this->assertText(t('"_devel_silent" route requirement forces devel to be inactive.'));
$this->assertNoText('Page execution time was');
// Ensure that devel doesn't interfere with non html response (e.g JsonResponse).
$response = $this->drupalGet('devel-silent/json');
$this->assertResponse(200);
$expected = ['data' => 'Devel is active only on HtmlResponse.'];
$this->assertIdentical(Json::decode($response), $expected);
// Ensure that devel doesn't interfere with private image style creation
// and with BinaryFileResponse response.
$style = ImageStyle::create([
'name' => 'zyx',
'label' => $this->randomString(),
]);
$style->save();
$image = current($this->drupalGetTestFiles('image'));
$image_uri = file_unmanaged_copy($image->uri, 'private://');
// Let the devel_test module know about this file, so it can claim
// ownership in hook_file_download().
\Drupal::state()->set('devel.test_file_download', $image_uri);
$this->drupalGet($style->buildUrl($image_uri));
$this->assertResponse(200);
$this->assertRaw(file_get_contents($style->buildUri($image_uri)), 'URL returns expected file.');
$this->assertNoText('Page execution time was');
// Ensure that devel doesn't interfere with private files and with
// BinaryFileResponse response.
$file = File::create([
'uid' => $web_user->id(),
'filename' => 'drupal.txt',
'uri' => 'private://devel.txt',
'filemime' => 'text/plain',
'status' => FILE_STATUS_PERMANENT,
]);
file_put_contents($file->getFileUri(), 'Hello world!');
$file->save();
// Let the image_module_test module know about this file, so it can claim
// ownership in hook_file_download().
\Drupal::state()->set('devel.test_file_download', $file->getFileUri());
$this->drupalGet($file->url());
$this->assertResponse(200);
$this->assertRaw(file_get_contents($file->getFileUri()), 'URL returns expected file.');
$this->assertNoText('Page execution time was');
}
}
......@@ -27,3 +27,13 @@ function devel_test_entity_view_mode_info_alter(&$view_modes) {
}
}
}
/**
* Implements hook_file_download().
*/
function devel_test_file_download($uri) {
$default_uri = \Drupal::state()->get('devel.test_file_download') ?: FALSE;
if ($default_uri === $uri) {
return array('X-Owned-By' => 'devel_test');
}
}
devel.silent_global:
path: '/devel-silent/global-shoutdown'
defaults:
_controller: '\Drupal\devel_test\Controller\DevelSilentTestContoller::globalShoutdown'
_title: 'Devel shoutdown'
requirements:
_permission: 'access devel information'
devel.silent_route_parameter:
path: '/devel-silent/route-requirement'
defaults:
_controller: '\Drupal\devel_test\Controller\DevelSilentTestContoller::develSilentRouteRequirement'
_title: 'Devel settings'
requirements:
_permission: 'access devel information'
_devel_silent: 'TRUE'
devel.silent_json:
path: '/devel-silent/json'
defaults:
_controller: '\Drupal\devel_test\Controller\DevelSilentTestContoller::jsonResponse'
_title: 'Devel settings'
requirements:
_permission: 'access devel information'
route_callbacks:
- '\Drupal\devel_test\Routing\DevelEntityTestRoutes::routes'
<?php
/**
* @file
* Contains \Drupal\devel_test\Controller\DevelSilentTestContoller.
*/
namespace Drupal\devel_test\Controller;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\JsonResponse;
/**
* Returns responses for devel module routes.
*/
class DevelSilentTestContoller extends ControllerBase {
/**
* Tests that devel is disabled if $GLOBALS['devel_shutdown'] is set.
*
* @return array
* A render array.
*/
public function globalShoutdown() {
$GLOBALS['devel_shutdown'] = FALSE;
return [
'#markup' => $this->t('$GLOBALS[\'devel_shutdown\'] = FALSE forces devel to be inactive.'),
];
}
/**
* Tests that devel is disabled if response come from routes that are
* declared with '_devel_silent' requirement.
*
* @return array
* A render array.
*/
public function develSilentRouteRequirement() {
return [
'#markup' => $this->t('"_devel_silent" route requirement forces devel to be inactive.'),
];
}
/**
* Tests that devel is disabled if is reyurned a JsonResponse response.
*
* @return \Symfony\Component\HttpFoundation\JsonResponse
* A json response.
*/
public function jsonResponse() {
$data = ['data' => 'Devel is active only on HtmlResponse.'];
return new JsonResponse($data);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment