[WebProfiler] [HttpKernel] profile redirections
closes #17501. The profiler stores the current request attributes in a current session when a `RedirectionResponse` is returned. So the next request profile will inherit the previous request attributes. The main profiler layout displays a shortcut to a previous redirection profile, along with some useful informations. The web debug toolbar shows a notifying icon, meaning a shortcut to a redirection profile is available in the request toolbar panel.
This commit is contained in:
parent
06eb52cb75
commit
0a1b2841f7
@ -2,33 +2,30 @@
|
||||
|
||||
{% block toolbar %}
|
||||
{% set request_handler %}
|
||||
{% if collector.controller.class is defined %}
|
||||
{% set link = collector.controller.file|file_link(collector.controller.line) %}
|
||||
{% if link %}<a href="{{ link }}" title="{{ collector.controller.file }}">{% else %}<span>{% endif %}
|
||||
|
||||
{{ collector.controller.class|abbr_class|striptags }}
|
||||
|
||||
{%- if collector.controller.method -%}
|
||||
:: {{ collector.controller.method }}
|
||||
{%- endif -%}
|
||||
|
||||
{% if link %}</a>{% else %}</span>{% endif %}
|
||||
{% else %}
|
||||
<span>{{ collector.controller }}</span>
|
||||
{% endif %}
|
||||
{% import _self as helper %}
|
||||
{{ helper.set_handler(collector.controller) }}
|
||||
{% endset %}
|
||||
|
||||
{% if collector.redirect %}
|
||||
{% set redirect_handler %}
|
||||
{% import _self as helper %}
|
||||
{{ helper.set_handler(collector.redirect.controller, collector.redirect.route, 'GET' != collector.redirect.method ? collector.redirect.method) }}
|
||||
{% endset %}
|
||||
{% endif %}
|
||||
|
||||
{% set request_status_code_color = (collector.statuscode >= 400) ? 'red' : (collector.statuscode >= 300) ? 'yellow' : 'green' %}
|
||||
|
||||
{% set icon %}
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{{ request_status_code_color }}">{{ collector.statuscode }}</span>
|
||||
{% if collector.route %}
|
||||
{% if collector.redirect %}{{ include('@WebProfiler/Icon/redirect.svg') }}{% endif %}
|
||||
<span class="sf-toolbar-label">@</span>
|
||||
<span class="sf-toolbar-value sf-toolbar-info-piece-additional">{{ collector.route }}</span>
|
||||
{% endif %}
|
||||
{% endset %}
|
||||
|
||||
{% set text %}
|
||||
<div class="sf-toolbar-info-group">
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>HTTP status</b>
|
||||
<span>{{ collector.statuscode }} {{ collector.statustext }}</span>
|
||||
@ -39,12 +36,12 @@
|
||||
<span>{{ request_handler }}</span>
|
||||
</div>
|
||||
|
||||
{% if collector.controller.class is defined %}
|
||||
{% if collector.controller.class is defined -%}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Controller class</b>
|
||||
<span>{{ collector.controller.class }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{%- endif %}
|
||||
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Route name</b>
|
||||
@ -55,6 +52,22 @@
|
||||
<b>Has session</b>
|
||||
<span>{% if collector.sessionmetadata|length %}yes{% else %}no{% endif %}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if redirect_handler is defined -%}
|
||||
<div class="sf-toolbar-info-group">
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>
|
||||
<span class="sf-toolbar-redirection-status sf-toolbar-status-yellow">{{ collector.redirect.status_code }}</span>
|
||||
Redirect from
|
||||
</b>
|
||||
<span>
|
||||
{{ redirect_handler }}
|
||||
(<a href="{{ path('_profiler', { token: collector.redirect.token }) }}">{{ collector.redirect.token }}</a>)
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endset %}
|
||||
|
||||
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url }) }}
|
||||
@ -224,3 +237,22 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% macro set_handler(controller, route, method) %}
|
||||
{% if controller.class is defined -%}
|
||||
{%- if method|default(false) %}<span class="sf-toolbar-status sf-toolbar-redirection-method">{{ method }}</span>{% endif -%}
|
||||
{%- set link = controller.file|file_link(controller.line) %}
|
||||
{%- if link %}<a href="{{ link }}" title="{{ controller.file }}">{% else %}<span>{% endif %}
|
||||
|
||||
{%- if route|default(false) -%}
|
||||
@{{ route }}
|
||||
{%- else -%}
|
||||
{{- controller.class|abbr_class|striptags -}}
|
||||
{{- controller.method ? ' :: ' ~ controller.method -}}
|
||||
{%- endif -%}
|
||||
|
||||
{%- if link %}</a>{% else %}</span>{% endif %}
|
||||
{%- else -%}
|
||||
<span>{{ route|default(controller) }}</span>
|
||||
{%- endif %}
|
||||
{% endmacro %}
|
||||
|
@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path style="fill:#aaa" d="M23.06,7.83L14,0.38a1.25,1.25,0,0,0-2,.89V4.09a13.61,13.61,0,0,1-2.2.61l-1.3.47C8,
|
||||
5.35,7.59,5.6,7.12,5.81l-0.69.35-0.72.45a10.62,10.62,0,0,0-1.41,1A13.22,13.22,0,0,0,3,8.82a15.31,15.31,
|
||||
0,0,0-1.13,1.46A17.63,17.63,0,0,0,1,11.93c-0.18.58-.34,1.16-0.48,1.71S0.45,14.76.43,15.29a10.2,10.2,0,0,0,.16,
|
||||
1.5,5.72,5.72,0,0,0,.33,1.34c0.14,0.41.26,0.82,0.42,1.19,0.37,0.71.67,1.38,1,1.94l1,1.46c0.32,0.41.63,0.75,0.87,
|
||||
1s0.51,0.09.43-.22-0.23-.75-0.35-1.23L4,20.69c-0.1-.58-0.09-1.22-0.14-1.86,0-.32.05-0.65,0.08-1a3.44,3.44,0,0,1,
|
||||
.16-1A6.44,6.44,0,0,1,4.41,16l0.41-.8c0.2-.22.38-0.44,0.55-0.65L6,14c0.23-.14.5-0.24,0.72-0.37a7.52,7.52,0,0,1,
|
||||
.79-0.25,4.48,4.48,0,0,1,.84-0.15l0.41-.06H9.22c0.3,0,.56,0,0.85,0l0.72,0.07a3.77,3.77,0,0,1,1.2.21v3.17a1.25,
|
||||
1.25,0,0,0,2,.89l9-7.45A1.46,1.46,0,0,0,23.06,7.83Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 980 B |
@ -19,6 +19,30 @@
|
||||
{% endif %}
|
||||
</h2>
|
||||
|
||||
{% if profile.collectors.request is defined and profile.collectors.request.redirect -%}
|
||||
{%- set redirect = profile.collectors.request.redirect -%}
|
||||
{%- set controller = redirect.controller -%}
|
||||
{%- set redirect_route = '@' ~ redirect.route %}
|
||||
<dl class="metadata">
|
||||
<dt>
|
||||
<span class="label">{{ redirect.status_code }}</span>
|
||||
Redirect from
|
||||
</dt>
|
||||
<dd>
|
||||
{{ 'GET' != redirect.method ? redirect.method }}
|
||||
{% if redirect.controller.class is defined -%}
|
||||
{%- set link = controller.file|file_link(controller.line) -%}
|
||||
{% if link %}<a href="{{ link }}" title="{{ controller.file }}">{% endif -%}
|
||||
{{ redirect_route }}
|
||||
{%- if link %}</a>{% endif -%}
|
||||
{%- else -%}
|
||||
{{ redirect_route }}
|
||||
{%- endif %}
|
||||
(<a href="{{ path('_profiler', { token: redirect.token }) }}">{{ redirect.token }}</a>)
|
||||
</dd>
|
||||
</dl>
|
||||
{%- endif %}
|
||||
|
||||
<dl class="metadata">
|
||||
<dt>Method</dt>
|
||||
<dd>{{ profile.method|upper }}</dd>
|
||||
|
@ -483,11 +483,11 @@ tr.status-warning td {
|
||||
#summary .status-error { background: {{ colors.error|raw }}; }
|
||||
|
||||
#summary .status-success h2,
|
||||
#summary .status-success h2 a,
|
||||
#summary .status-success a,
|
||||
#summary .status-warning h2,
|
||||
#summary .status-warning h2 a,
|
||||
#summary .status-warning a,
|
||||
#summary .status-error h2,
|
||||
#summary .status-error h2 a {
|
||||
#summary .status-error a {
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
@ -510,6 +510,10 @@ tr.status-warning td {
|
||||
margin: 0 1.5em 0 0;
|
||||
}
|
||||
|
||||
#summary dl.metadata .label {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
{# Sidebar
|
||||
========================================================================= #}
|
||||
#sidebar {
|
||||
|
@ -36,7 +36,7 @@
|
||||
.sf-toolbarreset {
|
||||
background-color: #222;
|
||||
bottom: 0;
|
||||
box-shadow: 0 -1px 0px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
|
||||
color: #EEE;
|
||||
font: 11px Arial, sans-serif;
|
||||
left: 0;
|
||||
@ -138,7 +138,7 @@
|
||||
|
||||
.sf-toolbar-block .sf-toolbar-info-piece .sf-toolbar-status {
|
||||
padding: 2px 5px;
|
||||
margin-bottom: 0px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.sf-toolbar-block .sf-toolbar-info-piece .sf-toolbar-status + .sf-toolbar-status {
|
||||
margin-left: 4px;
|
||||
@ -232,6 +232,16 @@
|
||||
.sf-toolbar-block-request .sf-toolbar-info-piece a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sf-toolbar-block-request .sf-toolbar-redirection-status {
|
||||
font-weight: normal;
|
||||
padding: 2px 4px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.sf-toolbar-block-request .sf-toolbar-info-piece span.sf-toolbar-redirection-method {
|
||||
font-size: 12px;
|
||||
height: 17px;
|
||||
line-height: 17px;
|
||||
}
|
||||
|
||||
.sf-toolbar-status-green .sf-toolbar-label,
|
||||
.sf-toolbar-status-yellow .sf-toolbar-label,
|
||||
@ -380,7 +390,7 @@
|
||||
|
||||
.sf-toolbarreset {
|
||||
bottom: auto;
|
||||
box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
|
||||
top: 0;
|
||||
}
|
||||
|
||||
@ -450,8 +460,11 @@
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
.sf-toolbar-block-request .sf-toolbar-status + .sf-toolbar-label {
|
||||
margin-left: 4px;
|
||||
.sf-toolbar-block-request .sf-toolbar-status {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.sf-toolbar-block-request .sf-toolbar-icon svg + .sf-toolbar-label {
|
||||
margin-left: 0;
|
||||
}
|
||||
.sf-toolbar-block-request .sf-toolbar-label + .sf-toolbar-value {
|
||||
margin-right: 10px;
|
||||
|
@ -122,48 +122,25 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
|
||||
}
|
||||
|
||||
if (isset($this->controllers[$request])) {
|
||||
$controller = $this->controllers[$request];
|
||||
if (is_array($controller)) {
|
||||
try {
|
||||
$r = new \ReflectionMethod($controller[0], $controller[1]);
|
||||
$this->data['controller'] = array(
|
||||
'class' => is_object($controller[0]) ? get_class($controller[0]) : $controller[0],
|
||||
'method' => $controller[1],
|
||||
'file' => $r->getFileName(),
|
||||
'line' => $r->getStartLine(),
|
||||
);
|
||||
} catch (\ReflectionException $e) {
|
||||
if (is_callable($controller)) {
|
||||
// using __call or __callStatic
|
||||
$this->data['controller'] = array(
|
||||
'class' => is_object($controller[0]) ? get_class($controller[0]) : $controller[0],
|
||||
'method' => $controller[1],
|
||||
'file' => 'n/a',
|
||||
'line' => 'n/a',
|
||||
);
|
||||
}
|
||||
}
|
||||
} elseif ($controller instanceof \Closure) {
|
||||
$r = new \ReflectionFunction($controller);
|
||||
$this->data['controller'] = array(
|
||||
'class' => $r->getName(),
|
||||
'method' => null,
|
||||
'file' => $r->getFileName(),
|
||||
'line' => $r->getStartLine(),
|
||||
);
|
||||
} elseif (is_object($controller)) {
|
||||
$r = new \ReflectionClass($controller);
|
||||
$this->data['controller'] = array(
|
||||
'class' => $r->getName(),
|
||||
'method' => null,
|
||||
'file' => $r->getFileName(),
|
||||
'line' => $r->getStartLine(),
|
||||
);
|
||||
} else {
|
||||
$this->data['controller'] = (string) $controller ?: 'n/a';
|
||||
}
|
||||
$this->data['controller'] = $this->parseController($this->controllers[$request]);
|
||||
unset($this->controllers[$request]);
|
||||
}
|
||||
|
||||
if ($request->hasSession() && $request->getSession()->has('sf_redirect')) {
|
||||
$this->data['redirect'] = $request->getSession()->get('sf_redirect');
|
||||
$request->getSession()->remove('sf_redirect');
|
||||
}
|
||||
|
||||
if ($request->hasSession() && $response->isRedirect()) {
|
||||
$request->getSession()->set('sf_redirect', array(
|
||||
'token' => $response->headers->get('x-debug-token'),
|
||||
'route' => $request->attributes->get('_route', 'n/a'),
|
||||
'method' => $request->getMethod(),
|
||||
'controller' => $this->parseController($request->attributes->get('_controller')),
|
||||
'status_code' => $statusCode,
|
||||
'status_text' => Response::$statusTexts[(int) $statusCode],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
public function getPathInfo()
|
||||
@ -276,15 +253,27 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the controller.
|
||||
* Gets the parsed controller.
|
||||
*
|
||||
* @return string The controller as a string
|
||||
* @return array|string The controller as a string or array of data
|
||||
* with keys 'class', 'method', 'file' and 'line'
|
||||
*/
|
||||
public function getController()
|
||||
{
|
||||
return $this->data['controller'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the previous request attributes.
|
||||
*
|
||||
* @return array|bool A legacy array of data from the previous redirection response
|
||||
* or false otherwise
|
||||
*/
|
||||
public function getRedirect()
|
||||
{
|
||||
return isset($this->data['redirect']) ? $this->data['redirect'] : false;
|
||||
}
|
||||
|
||||
public function onKernelController(FilterControllerEvent $event)
|
||||
{
|
||||
$this->controllers[$event->getRequest()] = $event->getController();
|
||||
@ -339,4 +328,65 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
|
||||
|
||||
return $cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a controller.
|
||||
*
|
||||
* @param mixed $controller The controller to parse
|
||||
*
|
||||
* @return array|string An array of controller data or a simple string
|
||||
*/
|
||||
private function parseController($controller)
|
||||
{
|
||||
if (is_string($controller) && false !== strpos($controller, '::')) {
|
||||
$controller = explode('::', $controller);
|
||||
}
|
||||
|
||||
if (is_array($controller)) {
|
||||
try {
|
||||
$r = new \ReflectionMethod($controller[0], $controller[1]);
|
||||
|
||||
return array(
|
||||
'class' => is_object($controller[0]) ? get_class($controller[0]) : $controller[0],
|
||||
'method' => $controller[1],
|
||||
'file' => $r->getFileName(),
|
||||
'line' => $r->getStartLine(),
|
||||
);
|
||||
} catch (\ReflectionException $e) {
|
||||
if (is_callable($controller)) {
|
||||
// using __call or __callStatic
|
||||
return array(
|
||||
'class' => is_object($controller[0]) ? get_class($controller[0]) : $controller[0],
|
||||
'method' => $controller[1],
|
||||
'file' => 'n/a',
|
||||
'line' => 'n/a',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($controller instanceof \Closure) {
|
||||
$r = new \ReflectionFunction($controller);
|
||||
|
||||
return array(
|
||||
'class' => $r->getName(),
|
||||
'method' => null,
|
||||
'file' => $r->getFileName(),
|
||||
'line' => $r->getStartLine(),
|
||||
);
|
||||
}
|
||||
|
||||
if (is_object($controller)) {
|
||||
$r = new \ReflectionClass($controller);
|
||||
|
||||
return array(
|
||||
'class' => $r->getName(),
|
||||
'method' => null,
|
||||
'file' => $r->getFileName(),
|
||||
'line' => $r->getStartLine(),
|
||||
);
|
||||
}
|
||||
|
||||
return (string) $controller ?: 'n/a';
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ class RequestDataCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
'"Regular" callable',
|
||||
array($this, 'testControllerInspection'),
|
||||
array(
|
||||
'class' => 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest',
|
||||
'class' => __NAMESPACE__.'\RequestDataCollectorTest',
|
||||
'method' => 'testControllerInspection',
|
||||
'file' => __FILE__,
|
||||
'line' => $r1->getStartLine(),
|
||||
@ -86,8 +86,13 @@ class RequestDataCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
array(
|
||||
'Static callback as string',
|
||||
'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest::staticControllerMethod',
|
||||
'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest::staticControllerMethod',
|
||||
__NAMESPACE__.'\RequestDataCollectorTest::staticControllerMethod',
|
||||
array(
|
||||
'class' => 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest',
|
||||
'method' => 'staticControllerMethod',
|
||||
'file' => __FILE__,
|
||||
'line' => $r2->getStartLine(),
|
||||
),
|
||||
),
|
||||
|
||||
array(
|
||||
|
Reference in New Issue
Block a user