feature #19614 [HttpKernel] Use VarDumper in the profiler (wouterj, nicolas-grekas)
This PR was merged into the 3.2-dev branch. Discussion ---------- [HttpKernel] Use VarDumper in the profiler | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | yes | Tests pass? | yes | Fixed tickets | part of #18149 | License | MIT | Doc PR | - /cc @javiereguiluz @nicolas-grekas As you're the main maintainers of the changed features. Summary --- * The `varToString()` method is deprecated in favor of `cloneVar()` (using the VarCloner) and `dump()` (using the VarDumper). This allows to show more detailed and better formatted data in the profiler. * The `Data` class of VarDumper is made serializable, to reduce the size of the stored profiler data. Screenshots --- ![sf-profiler-dumper](https://cloud.githubusercontent.com/assets/749025/17651142/9bcddc14-6260-11e6-80f6-81b84c82c0a3.png) Further Improvements --- * Change the dump colors (I've now implemented a very basic light theme, but my colorskills are close to zero, so a proper designer should look at it) Commits -------eddecbd
[HttpKernel] Use VarDumper in the Logs&Events panels of the profiler41a7649
[HttpKernel] Use VarDumper in the profiler
This commit is contained in:
commit
bfe0af5afe
@ -38,8 +38,14 @@ Form
|
||||
FrameworkBundle
|
||||
---------------
|
||||
|
||||
* The service `serializer.mapping.cache.doctrine.apc` is deprecated. APCu should now
|
||||
be automatically used when available.
|
||||
* The service `serializer.mapping.cache.doctrine.apc` is deprecated. APCu should now
|
||||
be automatically used when available.
|
||||
|
||||
HttpKernel
|
||||
----------
|
||||
|
||||
* `DataCollector::varToString()` is deprecated and will be removed in Symfony
|
||||
4.0. Use the `cloneVar()` method instead.
|
||||
|
||||
HttpFoundation
|
||||
---------------
|
||||
|
@ -155,6 +155,8 @@ HttpKernel
|
||||
have your own `ControllerResolverInterface` implementation, you should
|
||||
inject an `ArgumentResolverInterface` instance.
|
||||
|
||||
* The `DataCollector::varToString()` method has been removed in favor of `cloneVar()`.
|
||||
|
||||
Serializer
|
||||
----------
|
||||
|
||||
|
@ -45,7 +45,7 @@
|
||||
|
||||
{% for dump in collector.getDumps('html') %}
|
||||
<div class="sf-dump sf-reset">
|
||||
<h3>In
|
||||
<span class="metadata">In
|
||||
{% if dump.line %}
|
||||
{% set link = dump.file|file_link(dump.line) %}
|
||||
{% if link %}
|
||||
@ -56,10 +56,8 @@
|
||||
{% else %}
|
||||
{{ dump.name }}
|
||||
{% endif %}
|
||||
<small>line {{ dump.line }}</small>
|
||||
|
||||
<a class="text-small sf-toggle" data-toggle-selector="#sf-trace-{{ loop.index0 }}" data-toggle-alt-content="Hide code">Show code</a>
|
||||
</h3>
|
||||
line <a class="text-small sf-toggle" data-toggle-selector="#sf-trace-{{ loop.index0 }}">{{ dump.line }}</a>:
|
||||
</span>
|
||||
|
||||
<div class="sf-dump-compact hidden" id="sf-trace-{{ loop.index0 }}">
|
||||
<div class="trace">
|
||||
|
@ -24,6 +24,7 @@
|
||||
<service id="data_collector.form" class="Symfony\Component\Form\Extension\DataCollector\FormDataCollector">
|
||||
<tag name="data_collector" template="@WebProfiler/Collector/form.html.twig" id="form" priority="310" />
|
||||
<argument type="service" id="data_collector.form.extractor" />
|
||||
<argument>false</argument>
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
@ -20,6 +20,7 @@ use Symfony\Component\Security\Core\Role\RoleInterface;
|
||||
use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator;
|
||||
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\DebugAccessDecisionManager;
|
||||
use Symfony\Component\VarDumper\Cloner\Data;
|
||||
|
||||
/**
|
||||
* SecurityDataCollector.
|
||||
@ -58,6 +59,7 @@ class SecurityDataCollector extends DataCollector
|
||||
$this->data = array(
|
||||
'enabled' => false,
|
||||
'authenticated' => false,
|
||||
'token' => null,
|
||||
'token_class' => null,
|
||||
'logout_url' => null,
|
||||
'user' => '',
|
||||
@ -69,6 +71,7 @@ class SecurityDataCollector extends DataCollector
|
||||
$this->data = array(
|
||||
'enabled' => true,
|
||||
'authenticated' => false,
|
||||
'token' => null,
|
||||
'token_class' => null,
|
||||
'logout_url' => null,
|
||||
'user' => '',
|
||||
@ -101,18 +104,24 @@ class SecurityDataCollector extends DataCollector
|
||||
$this->data = array(
|
||||
'enabled' => true,
|
||||
'authenticated' => $token->isAuthenticated(),
|
||||
'token' => $this->cloneVar($token),
|
||||
'token_class' => get_class($token),
|
||||
'logout_url' => $logoutUrl,
|
||||
'user' => $token->getUsername(),
|
||||
'roles' => array_map(function (RoleInterface $role) { return $role->getRole();}, $assignedRoles),
|
||||
'inherited_roles' => array_map(function (RoleInterface $role) { return $role->getRole(); }, $inheritedRoles),
|
||||
'roles' => $this->cloneVar(array_map(function (RoleInterface $role) { return $role->getRole();}, $assignedRoles)),
|
||||
'inherited_roles' => $this->cloneVar(array_map(function (RoleInterface $role) { return $role->getRole(); }, $inheritedRoles)),
|
||||
'supports_role_hierarchy' => null !== $this->roleHierarchy,
|
||||
);
|
||||
}
|
||||
|
||||
// collect voters and access decision manager information
|
||||
if ($this->accessDecisionManager instanceof DebugAccessDecisionManager) {
|
||||
$this->data['access_decision_log'] = $this->accessDecisionManager->getDecisionLog();
|
||||
$this->data['access_decision_log'] = array_map(function ($decision) {
|
||||
$decision['object'] = $this->cloneVar($decision['object']);
|
||||
|
||||
return $decision;
|
||||
}, $this->accessDecisionManager->getDecisionLog());
|
||||
|
||||
$this->data['voter_strategy'] = $this->accessDecisionManager->getStrategy();
|
||||
|
||||
foreach ($this->accessDecisionManager->getVoters() as $voter) {
|
||||
@ -196,6 +205,16 @@ class SecurityDataCollector extends DataCollector
|
||||
return $this->data['token_class'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full security token class as Data object.
|
||||
*
|
||||
* @return Data
|
||||
*/
|
||||
public function getToken()
|
||||
{
|
||||
return $this->data['token'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the provider key (i.e. the name of the active firewall).
|
||||
*
|
||||
|
@ -3,7 +3,7 @@
|
||||
{% block page_title 'Security' %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% if collector.tokenClass %}
|
||||
{% if collector.token %}
|
||||
{% set is_authenticated = collector.enabled and collector.authenticated %}
|
||||
{% set color_code = is_authenticated ? '' : 'yellow' %}
|
||||
{% else %}
|
||||
@ -16,7 +16,7 @@
|
||||
{% endset %}
|
||||
|
||||
{% set text %}
|
||||
{% if collector.tokenClass %}
|
||||
{% if collector.token %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Logged in as</b>
|
||||
<span>{{ collector.user }}</span>
|
||||
@ -27,7 +27,7 @@
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{{ is_authenticated ? 'green' : 'red' }}">{{ is_authenticated ? 'Yes' : 'No' }}</span>
|
||||
</div>
|
||||
|
||||
{% if collector.tokenClass != null %}
|
||||
{% if collector.token != null %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Token class</b>
|
||||
<span>{{ collector.tokenClass|abbr_class }}</span>
|
||||
@ -54,7 +54,7 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
<span class="label {{ not collector.enabled or not collector.tokenClass ? 'disabled' }}">
|
||||
<span class="label {{ not collector.enabled or not collector.token ? 'disabled' }}">
|
||||
<span class="icon">{{ include('@Security/Collector/icon.svg') }}</span>
|
||||
<strong>Security</strong>
|
||||
</span>
|
||||
@ -63,7 +63,7 @@
|
||||
{% block panel %}
|
||||
<h2>Security Token</h2>
|
||||
|
||||
{% if collector.tokenClass %}
|
||||
{% if collector.token %}
|
||||
<div class="metrics">
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.user == 'anon.' ? 'Anonymous' : collector.user }}</span>
|
||||
@ -87,7 +87,7 @@
|
||||
<tr>
|
||||
<th>Roles</th>
|
||||
<td>
|
||||
{{ collector.roles is empty ? 'none' : collector.roles|yaml_encode }}
|
||||
{{ collector.roles is empty ? 'none' : profiler_dump(collector.roles, maxDepth=1) }}
|
||||
|
||||
{% if not collector.authenticated and collector.roles is empty %}
|
||||
<p class="help">User is not authenticated probably because they have no roles.</p>
|
||||
@ -98,14 +98,14 @@
|
||||
{% if collector.supportsRoleHierarchy %}
|
||||
<tr>
|
||||
<th>Inherited Roles</th>
|
||||
<td>{{ collector.inheritedRoles is empty ? 'none' : collector.inheritedRoles|yaml_encode }}</td>
|
||||
<td>{{ collector.inheritedRoles is empty ? 'none' : profiler_dump(collector.inheritedRoles, maxDepth=1) }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
{% if collector.tokenClass %}
|
||||
{% if collector.token %}
|
||||
<tr>
|
||||
<th>Token class</th>
|
||||
<td>{{ collector.tokenClass }}</td>
|
||||
<th>Token</th>
|
||||
<td>{{ profiler_dump(collector.token) }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
|
@ -62,8 +62,12 @@ class SecurityDataCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertTrue($collector->isAuthenticated());
|
||||
$this->assertSame('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $collector->getTokenClass());
|
||||
$this->assertTrue($collector->supportsRoleHierarchy());
|
||||
$this->assertSame($normalizedRoles, $collector->getRoles());
|
||||
$this->assertSame($inheritedRoles, $collector->getInheritedRoles());
|
||||
$this->assertSame($normalizedRoles, $collector->getRoles()->getRawData()[1]);
|
||||
if ($inheritedRoles) {
|
||||
$this->assertSame($inheritedRoles, $collector->getInheritedRoles()->getRawData()[1]);
|
||||
} else {
|
||||
$this->assertSame($inheritedRoles, $collector->getInheritedRoles()->getRawData()[0][0]);
|
||||
}
|
||||
$this->assertSame('hhamon', $collector->getUser());
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
"require": {
|
||||
"php": ">=5.5.9",
|
||||
"symfony/security": "~3.2",
|
||||
"symfony/http-kernel": "~3.1",
|
||||
"symfony/http-kernel": "~3.2",
|
||||
"symfony/polyfill-php70": "~1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
@ -34,6 +34,7 @@
|
||||
"symfony/twig-bridge": "~2.8|~3.0",
|
||||
"symfony/process": "~2.8|~3.0",
|
||||
"symfony/validator": "~2.8|~3.0",
|
||||
"symfony/var-dumper": "~3.2",
|
||||
"symfony/yaml": "~2.8|~3.0",
|
||||
"symfony/expression-language": "~2.8|~3.0",
|
||||
"doctrine/doctrine-bundle": "~1.4",
|
||||
|
@ -75,38 +75,7 @@
|
||||
|
||||
<tr>
|
||||
<td class="text-right">{{ listener.priority|default('-') }}</td>
|
||||
<td class="font-normal">
|
||||
{% if listener.type == 'Closure' %}
|
||||
|
||||
Closure
|
||||
<span class="text-muted text-small">(there is no class or file information)</span>
|
||||
|
||||
{% elseif listener.type == 'Function' %}
|
||||
|
||||
{% set link = listener.file|file_link(listener.line) %}
|
||||
{% if link %}
|
||||
<a href="{{ link }}">{{ listener.function }}()</a>
|
||||
<span class="text-muted text-small">({{ listener.file }})</span>
|
||||
{% else %}
|
||||
{{ listener.function }}()
|
||||
<span class="text-muted newline text-small">{{ listener.file }} (line {{ listener.line }})</span>
|
||||
{% endif %}
|
||||
|
||||
{% elseif listener.type == "Method" %}
|
||||
|
||||
{% set link = listener.file|file_link(listener.line) %}
|
||||
{% set class_namespace = listener.class|split('\\', -1)|join('\\') %}
|
||||
|
||||
{% if link %}
|
||||
<a href="{{ link }}"><strong>{{ listener.class|abbr_class|striptags }}</strong>::{{ listener.method }}()</a>
|
||||
<span class="text-muted text-small">({{ listener.class }})</span>
|
||||
{% else %}
|
||||
<span>{{ class_namespace }}\</span><strong>{{ listener.class|abbr_class|striptags }}</strong>::{{ listener.method }}()
|
||||
<span class="text-muted newline text-small">{{ listener.file }} (line {{ listener.line }})</span>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="font-normal">{{ profiler_dump(listener.data) }}</td>
|
||||
</tr>
|
||||
|
||||
{% if loop.last %}
|
||||
|
@ -152,9 +152,6 @@
|
||||
margin-top: -9px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
.form-type {
|
||||
color: #999;
|
||||
}
|
||||
.badge-error {
|
||||
float: right;
|
||||
background: #B0413E;
|
||||
@ -198,7 +195,7 @@
|
||||
|
||||
<div id="tree-details-container">
|
||||
{% for formName, formData in collector.data.forms %}
|
||||
{{ form_tree_details(formName, formData, collector.data.forms_by_hash) }}
|
||||
{{ form_tree_details(formName, formData, collector.data.forms_by_hash, loop.first) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
@ -442,7 +439,7 @@
|
||||
{% endif %}
|
||||
|
||||
<span {% if has_error or data.has_children_error|default(false) %}class="has-error"{% endif %}>
|
||||
{{ name|default('(no name)') }} {% if data.type_class is defined %}[<abbr title="{{ data.type_class }}">{{ data.type_class|split('\\')|last }}</abbr>]{% endif %}
|
||||
{{ name|default('(no name)') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -456,14 +453,11 @@
|
||||
</li>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro form_tree_details(name, data, forms_by_hash) %}
|
||||
{% macro form_tree_details(name, data, forms_by_hash, show) %}
|
||||
{% import _self as tree %}
|
||||
<div class="tree-details" {% if data.id is defined %}id="{{ data.id }}-details"{% endif %}>
|
||||
<h2>
|
||||
{{ name|default('(no name)') }}
|
||||
{% if data.type_class is defined and data.type is defined %}
|
||||
<span class="form-type">[<abbr title="{{ data.type_class }}">{{ data.type }}</abbr>]</span>
|
||||
{% endif %}
|
||||
<div class="tree-details{% if not show|default(false) %} hidden{% endif %}" {% if data.id is defined %}id="{{ data.id }}-details"{% endif %}>
|
||||
<h2 class="dump-inline">
|
||||
{{ name|default('(no name)') }} ({{ profiler_dump(data.type_class) }})
|
||||
</h2>
|
||||
|
||||
{% if data.errors is defined and data.errors|length > 0 %}
|
||||
@ -496,29 +490,12 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% for trace in error.trace %}
|
||||
{% if not loop.first %}
|
||||
<span class="newline">Caused by:</span>
|
||||
{% endif %}
|
||||
|
||||
{% if trace.root is defined %}
|
||||
<strong class="newline">{{ trace.class }}</strong>
|
||||
<pre>
|
||||
{{- trace.root -}}
|
||||
{%- if trace.path is not empty -%}
|
||||
{%- if trace.path|first != '[' %}.{% endif -%}
|
||||
{{- trace.path -}}
|
||||
{%- endif %} = {{ trace.value -}}
|
||||
</pre>
|
||||
{% elseif trace.message is defined %}
|
||||
<strong class="newline">{{ trace.class }}</strong>
|
||||
<pre>{{ trace.message }}</pre>
|
||||
{% else %}
|
||||
<pre>{{ trace }}</pre>
|
||||
{% endif %}
|
||||
{% if error.trace %}
|
||||
<span class="newline">Caused by:</span>
|
||||
{{ profiler_dump(error.trace, maxDepth=2) }}
|
||||
{% else %}
|
||||
<em>Unknown.</em>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@ -547,7 +524,7 @@
|
||||
<th class="font-normal" scope="row">Model Format</th>
|
||||
<td>
|
||||
{% if data.default_data.model is defined %}
|
||||
{{ data.default_data.model }}
|
||||
{{ profiler_dump(data.default_data.model) }}
|
||||
{% else %}
|
||||
<em class="font-normal text-muted">same as normalized format</em>
|
||||
{% endif %}
|
||||
@ -555,13 +532,13 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="font-normal" scope="row">Normalized Format</th>
|
||||
<td>{{ data.default_data.norm }}</td>
|
||||
<td>{{ profiler_dump(data.default_data.norm) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="font-normal" scope="row">View Format</th>
|
||||
<td>
|
||||
{% if data.default_data.view is defined %}
|
||||
{{ data.default_data.view }}
|
||||
{{ profiler_dump(data.default_data.view) }}
|
||||
{% else %}
|
||||
<em class="font-normal text-muted">same as normalized format</em>
|
||||
{% endif %}
|
||||
@ -593,7 +570,7 @@
|
||||
<th class="font-normal" scope="row">View Format</th>
|
||||
<td>
|
||||
{% if data.submitted_data.view is defined %}
|
||||
{{ data.submitted_data.view }}
|
||||
{{ profiler_dump(data.submitted_data.view) }}
|
||||
{% else %}
|
||||
<em class="font-normal text-muted">same as normalized format</em>
|
||||
{% endif %}
|
||||
@ -601,13 +578,13 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="font-normal" scope="row">Normalized Format</th>
|
||||
<td>{{ data.submitted_data.norm }}</td>
|
||||
<td>{{ profiler_dump(data.submitted_data.norm) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="font-normal" scope="row">Model Format</th>
|
||||
<td>
|
||||
{% if data.submitted_data.model is defined %}
|
||||
{{ data.submitted_data.model }}
|
||||
{{ profiler_dump(data.submitted_data.model) }}
|
||||
{% else %}
|
||||
<em class="font-normal text-muted">same as normalized format</em>
|
||||
{% endif %}
|
||||
@ -644,12 +621,12 @@
|
||||
{% for option, value in data.passed_options %}
|
||||
<tr>
|
||||
<th>{{ option }}</th>
|
||||
<td>{{ value }}</td>
|
||||
<td>{{ profiler_dump(value) }}</td>
|
||||
<td>
|
||||
{% if data.resolved_options[option] is same as(value) %}
|
||||
{% if data.resolved_options[option] == value %}
|
||||
<em class="font-normal text-muted">same as passed value</em>
|
||||
{% else %}
|
||||
{{ data.resolved_options[option] }}
|
||||
{{ profiler_dump(data.resolved_options[option]) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
@ -683,7 +660,7 @@
|
||||
{% for option, value in data.resolved_options %}
|
||||
<tr>
|
||||
<th scope="row">{{ option }}</th>
|
||||
<td>{{ value }}</td>
|
||||
<td>{{ profiler_dump(value) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@ -710,7 +687,7 @@
|
||||
{% for variable, value in data.view_vars %}
|
||||
<tr>
|
||||
<th scope="row">{{ variable }}</th>
|
||||
<td>{{ value }}</td>
|
||||
<td>{{ profiler_dump(value) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
@ -3,12 +3,11 @@
|
||||
{% import _self as helper %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% if collector.counterrors or collector.countdeprecations or collector.countscreams %}
|
||||
{% if collector.counterrors or collector.countdeprecations or collector.countwarnings %}
|
||||
{% set icon %}
|
||||
{% set status_color = collector.counterrors ? 'red' : collector.countdeprecations ? 'yellow' : '' %}
|
||||
{% set error_count = collector.counterrors + collector.countdeprecations %}
|
||||
{% set status_color = collector.counterrors ? 'red' : 'yellow' %}
|
||||
{{ include('@WebProfiler/Icon/logger.svg') }}
|
||||
<span class="sf-toolbar-value">{{ error_count }}</span>
|
||||
<span class="sf-toolbar-value">{{ collector.counterrors ?: (collector.countdeprecations + collector.countwarnings) }}</span>
|
||||
{% endset %}
|
||||
|
||||
{% set text %}
|
||||
@ -18,13 +17,13 @@
|
||||
</div>
|
||||
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Deprecated Calls</b>
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.countdeprecations ? 'yellow' }}">{{ collector.countdeprecations|default(0) }}</span>
|
||||
<b>Warnings</b>
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.countwarnings ? 'yellow' }}">{{ collector.countwarnings|default(0) }}</span>
|
||||
</div>
|
||||
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Silenced Errors</b>
|
||||
<span class="sf-toolbar-status">{{ collector.countscreams|default(0) }}</span>
|
||||
<b>Deprecations</b>
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.countdeprecations ? 'yellow' }}">{{ collector.countdeprecations|default(0) }}</span>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
@ -33,12 +32,12 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
<span class="label label-status-{{ collector.counterrors ? 'error' : collector.countdeprecations ? 'warning' }} {{ collector.logs is empty ? 'disabled' }}">
|
||||
<span class="label label-status-{{ collector.counterrors ? 'error' : collector.countdeprecations or collector.countwarnings ? 'warning' }} {{ collector.logs is empty ? 'disabled' }}">
|
||||
<span class="icon">{{ include('@WebProfiler/Icon/logger.svg') }}</span>
|
||||
<strong>Logs</strong>
|
||||
{% if collector.counterrors or collector.countdeprecations %}
|
||||
{% if collector.counterrors or collector.countdeprecations or collector.countwarnings %}
|
||||
<span class="count">
|
||||
<span>{{ collector.counterrors ?: collector.countdeprecations }}</span>
|
||||
<span>{{ collector.counterrors ?: (collector.countdeprecations + collector.countwarnings) }}</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
@ -55,9 +54,9 @@
|
||||
{# sort collected logs in groups #}
|
||||
{% set deprecation_logs, debug_logs, info_and_error_logs, silenced_logs = [], [], [], [] %}
|
||||
{% for log in collector.logs %}
|
||||
{% if log.context.errorCount is defined and log.context.type is defined and log.context.type in ['E_DEPRECATED', 'E_USER_DEPRECATED'] %}
|
||||
{% if log.scream is defined and not log.scream %}
|
||||
{% set deprecation_logs = deprecation_logs|merge([log]) %}
|
||||
{% elseif log.context.scream is defined and log.context.scream == true %}
|
||||
{% elseif log.scream is defined and log.scream %}
|
||||
{% set silenced_logs = silenced_logs|merge([log]) %}
|
||||
{% elseif log.priorityName == 'DEBUG' %}
|
||||
{% set debug_logs = debug_logs|merge([log]) %}
|
||||
@ -68,7 +67,7 @@
|
||||
|
||||
<div class="sf-tabs">
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Info. & Errors <span class="badge">{{ info_and_error_logs|length }}</span></h3>
|
||||
<h3 class="tab-title">Info. & Errors <span class="badge status-{{ collector.counterrors ? 'error' : collector.countwarnings ? 'warning' }}">{{ collector.counterrors ?: info_and_error_logs|length }}</span></h3>
|
||||
|
||||
<div class="tab-content">
|
||||
{% if info_and_error_logs is empty %}
|
||||
@ -84,7 +83,7 @@
|
||||
<div class="tab">
|
||||
{# 'deprecation_logs|length' is not used because deprecations are
|
||||
now grouped and the group count doesn't match the message count #}
|
||||
<h3 class="tab-title">Deprecations <span class="badge">{{ collector.countdeprecations|default(0) }}</span></h3>
|
||||
<h3 class="tab-title">Deprecations <span class="badge status-{{ collector.countdeprecations ? 'warning' }}">{{ collector.countdeprecations|default(0) }}</span></h3>
|
||||
|
||||
<div class="tab-content">
|
||||
{% if deprecation_logs is empty %}
|
||||
@ -112,7 +111,7 @@
|
||||
</div>
|
||||
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Silenced Errors <span class="badge">{{ collector.countscreams|default(0) }}</span></h3>
|
||||
<h3 class="tab-title">Silenced PHP Notices<span class="badge">{{ collector.countscreams|default(0) }}</span></h3>
|
||||
|
||||
<div class="tab-content">
|
||||
{% if silenced_logs is empty %}
|
||||
@ -138,7 +137,7 @@
|
||||
<tr>
|
||||
<th>{{ show_level ? 'Level' : 'Time' }}</th>
|
||||
{% if channel_is_defined %}<th>Channel</th>{% endif %}
|
||||
<th>Message</th>
|
||||
<th class="full-width">Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@ -146,18 +145,24 @@
|
||||
{% for log in logs %}
|
||||
{% set css_class = is_deprecation ? ''
|
||||
: log.priorityName in ['CRITICAL', 'ERROR', 'ALERT', 'EMERGENCY'] ? 'status-error'
|
||||
: log.priorityName in ['NOTICE', 'WARNING'] ? 'status-warning'
|
||||
: log.priorityName == 'WARNING' ? 'status-warning'
|
||||
%}
|
||||
<tr class="{{ css_class }}">
|
||||
<td class="font-normal text-small">
|
||||
<td class="font-normal text-small" nowrap>
|
||||
{% if show_level %}
|
||||
<span class="colored text-bold nowrap">{{ log.priorityName }}</span>
|
||||
<span class="colored text-bold">{{ log.priorityName }}</span>
|
||||
{% endif %}
|
||||
<span class="text-muted nowrap newline">{{ log.timestamp|date('H:i:s') }}</span>
|
||||
<span class="text-muted newline">{{ log.timestamp|date('H:i:s') }}</span>
|
||||
</td>
|
||||
|
||||
{% if channel_is_defined %}
|
||||
<td class="font-normal text-small text-bold nowrap">{{ log.channel }}</td>
|
||||
<td class="font-normal text-small text-bold" nowrap>
|
||||
{{ log.channel }}
|
||||
{% if log.errorCount is defined and log.errorCount > 1 %}
|
||||
<span class="text-muted">({{ log.errorCount }} times)</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
{% endif %}
|
||||
|
||||
<td class="font-normal">{{ helper.render_log_message(category, loop.index, log, is_deprecation) }}</td>
|
||||
@ -168,69 +173,31 @@
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_log_message(category, log_index, log, is_deprecation = false) %}
|
||||
{{ log.message }}
|
||||
|
||||
{% if log.context.errorCount is defined and log.context.errorCount > 1 %}
|
||||
<span class="text-small text-bold">({{ log.context.errorCount }} times)</span>
|
||||
{% endif %}
|
||||
|
||||
{% if is_deprecation %}
|
||||
{% set trace = log.context.trace|default([]) %}
|
||||
{% set trace_id = 'sf-call-trace-' ~ category ~ '-' ~ log_index %}
|
||||
{{ log.message }}
|
||||
|
||||
{% set context_id = 'context-' ~ category ~ '-' ~ log_index %}
|
||||
|
||||
{% if trace %}
|
||||
<button class="btn-link text-small sf-toggle" data-toggle-selector="#{{ trace_id }}" data-toggle-alt-content="Hide stack trace">Show stack trace</button>
|
||||
{% endif %}
|
||||
<span class="metadata">
|
||||
<a class="btn btn-link text-small sf-toggle" data-toggle-selector="#{{ context_id }}" data-toggle-alt-content="Hide trace">Show trace</a>
|
||||
|
||||
{% for index, call in trace if index > 1 %}
|
||||
{% if index == 2 %}
|
||||
<ul class="sf-call-trace hidden" id="{{ trace_id }}">
|
||||
{% endif %}
|
||||
|
||||
{% if call.class is defined %}
|
||||
{% set from = call.class|abbr_class ~ '::' ~ call.function|abbr_method() %}
|
||||
{% elseif call.function is defined %}
|
||||
{% set from = call.function|abbr_method %}
|
||||
{% elseif call.file is defined %}
|
||||
{% set from = call.file %}
|
||||
{% else %}
|
||||
{% set from = '-' %}
|
||||
{% endif %}
|
||||
|
||||
{% set file_name = (call.file is defined and call.line is defined) ? call.file|replace({'\\': '/'})|split('/')|last %}
|
||||
|
||||
<li>
|
||||
{{ from|raw }}
|
||||
{% if file_name %}
|
||||
<span class="text-small">(called from {{ call.file|format_file(call.line, file_name)|raw }})</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
|
||||
{% if index == trace|length - 1 %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% if log.context is defined and log.context is not empty %}
|
||||
{% set context_id = 'context-' ~ category ~ '-' ~ log_index %}
|
||||
{% set context_dump = profiler_dump(log.context) %}
|
||||
|
||||
<div class="metadata">
|
||||
<strong>Context</strong>:
|
||||
|
||||
{% if context_dump|length > 120 %}
|
||||
{{ context_dump[:120] }} ...
|
||||
|
||||
<a class="btn-link text-small sf-toggle" data-toggle-selector="#{{ context_id }}" data-toggle-alt-content="Hide full context">Show full context</a>
|
||||
|
||||
<div id="{{ context_id }}" class="context">
|
||||
{{ dump(log.context) }}
|
||||
</div>
|
||||
{% else %}
|
||||
{{ context_dump }}
|
||||
{% endif %}
|
||||
<div id="{{ context_id }}" class="context sf-toggle-content sf-toggle-hidden">
|
||||
{{ profiler_dump(log.context.seek('exception').seek('\0Exception\0trace'), maxDepth=2) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% elseif log.context is defined and log.context is not empty %}
|
||||
{{ profiler_dump_log(log.message, log.context) }}
|
||||
|
||||
{% set context_id = 'context-' ~ category ~ '-' ~ log_index %}
|
||||
|
||||
<span class="metadata">
|
||||
<a class="btn btn-link text-small sf-toggle" data-toggle-selector="#{{ context_id }}" data-toggle-alt-content="Hide context">Show context</a>
|
||||
|
||||
<div id="{{ context_id }}" class="context sf-toggle-content sf-toggle-hidden">
|
||||
{{ profiler_dump(log.context, maxDepth=1) }}
|
||||
</div>
|
||||
</span>
|
||||
{% else %}
|
||||
{{ log.message }}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
@ -120,7 +120,7 @@
|
||||
<p>No GET parameters</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestquery }, with_context = false) }}
|
||||
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestquery, maxDepth: 1 }, with_context = false) }}
|
||||
{% endif %}
|
||||
|
||||
<h3>POST Parameters</h3>
|
||||
@ -130,7 +130,7 @@
|
||||
<p>No POST parameters</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestrequest }, with_context = false) }}
|
||||
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestrequest, maxDepth: 1 }, with_context = false) }}
|
||||
{% endif %}
|
||||
|
||||
<h3>Request Attributes</h3>
|
||||
@ -252,7 +252,7 @@
|
||||
{% for child in profile.children %}
|
||||
<h3>
|
||||
<a href="{{ path('_profiler', { token: child.token }) }}">
|
||||
{{- child.getcollector('request').requestattributes.get('_controller') -}}
|
||||
{{- child.getcollector('request').identifier -}}
|
||||
</a>
|
||||
<small>(token = {{ child.token }})</small>
|
||||
</h3>
|
||||
|
@ -128,7 +128,7 @@
|
||||
{% for child in profile.children %}
|
||||
{% set events = child.getcollector('time').events %}
|
||||
<h4>
|
||||
<a href="{{ path('_profiler', { token: child.token, panel: 'time' }) }}">{{ child.getcollector('request').requestattributes.get('_controller') }}</a>
|
||||
<a href="{{ path('_profiler', { token: child.token, panel: 'time' }) }}">{{ child.getcollector('request').identifier }}</a>
|
||||
<small>{{ events.__section__.duration }} ms</small>
|
||||
</h4>
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
{% for key in bag.keys|sort %}
|
||||
<tr>
|
||||
<th>{{ key }}</th>
|
||||
<td>{{ profiler_dump(bag.get(key)) }}</td>
|
||||
<td>{{ profiler_dump(bag.get(key), maxDepth=maxDepth|default(0)) }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
|
@ -190,6 +190,9 @@ table tbody ul {
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
@ -806,6 +809,8 @@ tr.status-warning td {
|
||||
.tab-content > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.tab-navigation li .badge.status-warning { background: {{ colors.warning|raw }}; color: #FFF; }
|
||||
.tab-navigation li .badge.status-error { background: {{ colors.error|raw }}; color: #FFF; }
|
||||
|
||||
{# Toggles
|
||||
========================================================================= #}
|
||||
@ -838,32 +843,8 @@ tr.status-warning td {
|
||||
{# Logger panel
|
||||
========================================================================= #}
|
||||
table.logs .metadata {
|
||||
color: #777;
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
table.logs .metadata strong {
|
||||
color: #222;
|
||||
}
|
||||
table.logs .metadata .context {
|
||||
background: #F5F5F5;
|
||||
color: #222;
|
||||
}
|
||||
table.logs .metadata .context pre {
|
||||
margin: 5px 0;
|
||||
padding: 5px 10px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
table.logs .sf-call-stack {
|
||||
margin: 1em 0 1em 1.5em;
|
||||
}
|
||||
table.logs .sf-call-stack li {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
table.logs .sf-call-stack abbr {
|
||||
border: none;
|
||||
}
|
||||
|
||||
{# Doctrine panel
|
||||
@ -889,43 +870,50 @@ table.logs .sf-call-stack abbr {
|
||||
#collector-content .sf-dump samp {
|
||||
{{ mixins.monospace_font|raw }}
|
||||
}
|
||||
#collector-content pre.sf-dump {
|
||||
background: #222;
|
||||
line-height: 1.4;
|
||||
margin-top: .5em;
|
||||
padding: 1em;
|
||||
}
|
||||
#collector-content .sf-dump h3 {
|
||||
font-size: 18px;
|
||||
margin: .5em 0 0;
|
||||
}
|
||||
#collector-content .sf-dump h3 a {
|
||||
#collector-content .sf-dump a {
|
||||
cursor: pointer;
|
||||
}
|
||||
#collector-content .sf-dump pre.sf-dump,
|
||||
#collector-content .sf-dump .trace {
|
||||
border: 1px solid #DDD;
|
||||
background: #FFF;
|
||||
padding: 10px;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
#collector-content pre.sf-dump { color: #CC7832; }
|
||||
#collector-content .sf-dump-str { color: #629755; }
|
||||
#collector-content .sf-dump-private,
|
||||
#collector-content .sf-dump-protected,
|
||||
#collector-content .sf-dump-public { color: #E0E0E0; }
|
||||
#collector-content .sf-dump-public { color: #262626; }
|
||||
#collector-content .sf-dump-note { color: #6897BB; }
|
||||
#collector-content .sf-dump-key { color: #A5C261; }
|
||||
#collector-content .sf-dump-key { color: #789339; }
|
||||
#collector-content .sf-dump-ref { color: #6E6E6E; }
|
||||
|
||||
#collector-content .sf-dump .trace {
|
||||
border: 1px solid #DDD;
|
||||
background: #FFF;
|
||||
padding: 10px;
|
||||
margin: 1em 0;
|
||||
#collector-content .sf-dump {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: none;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
#collector-content .dump-inline .sf-dump {
|
||||
display: inline;
|
||||
white-space: normal;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
#collector-content .dump-inline pre.sf-dump .sf-dump-ellipsis {
|
||||
width: 4em;
|
||||
}
|
||||
|
||||
#collector-content .sf-dump .trace {
|
||||
font-size: 12px;
|
||||
}
|
||||
#collector-content .sf-dump .trace code {
|
||||
font-size: 14px;
|
||||
}
|
||||
#collector-content .sf-dump .trace li {
|
||||
margin-bottom: 0;
|
||||
padding: 5px 0;
|
||||
padding: 0;
|
||||
}
|
||||
#collector-content .sf-dump .trace li.selected {
|
||||
background: rgba(255, 255, 153, 0.5);
|
||||
|
@ -12,31 +12,107 @@
|
||||
namespace Symfony\Bundle\WebProfilerBundle\Twig;
|
||||
|
||||
use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter;
|
||||
use Symfony\Component\VarDumper\Cloner\Data;
|
||||
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
|
||||
|
||||
/**
|
||||
* Twig extension for the profiler.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class WebProfilerExtension extends \Twig_Extension
|
||||
class WebProfilerExtension extends \Twig_Extension_Profiler
|
||||
{
|
||||
/**
|
||||
* @var ValueExporter
|
||||
*/
|
||||
private $valueExporter;
|
||||
|
||||
/**
|
||||
* @var HtmlDumper
|
||||
*/
|
||||
private $dumper;
|
||||
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
private $output;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $stackLevel = 0;
|
||||
|
||||
public function __construct(HtmlDumper $dumper = null)
|
||||
{
|
||||
$this->dumper = $dumper ?: new HtmlDumper();
|
||||
$this->dumper->setOutput($this->output = fopen('php://memory', 'r+b'));
|
||||
}
|
||||
|
||||
public function enter(\Twig_Profiler_Profile $profile)
|
||||
{
|
||||
++$this->stackLevel;
|
||||
}
|
||||
|
||||
public function leave(\Twig_Profiler_Profile $profile)
|
||||
{
|
||||
if (0 === --$this->stackLevel) {
|
||||
$this->dumper->setOutput($this->output = fopen('php://memory', 'r+b'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
$profilerDump = function (\Twig_Environment $env, $value, $maxDepth = 0) {
|
||||
return $value instanceof Data ? $this->dumpData($env, $value, $maxDepth) : twig_escape_filter($env, $this->dumpValue($value));
|
||||
};
|
||||
|
||||
return array(
|
||||
new \Twig_SimpleFunction('profiler_dump', array($this, 'dumpValue')),
|
||||
new \Twig_SimpleFunction('profiler_dump', $profilerDump, array('is_safe' => array('html'), 'needs_environment' => true)),
|
||||
new \Twig_SimpleFunction('profiler_dump_log', array($this, 'dumpLog'), array('is_safe' => array('html'), 'needs_environment' => true)),
|
||||
);
|
||||
}
|
||||
|
||||
public function dumpData(\Twig_Environment $env, Data $data, $maxDepth = 0)
|
||||
{
|
||||
$this->dumper->setCharset($env->getCharset());
|
||||
$this->dumper->dump($data, null, array(
|
||||
'maxDepth' => $maxDepth,
|
||||
));
|
||||
|
||||
$dump = stream_get_contents($this->output, -1, 0);
|
||||
rewind($this->output);
|
||||
ftruncate($this->output, 0);
|
||||
|
||||
return str_replace("\n</pre", '</pre', rtrim($dump));
|
||||
}
|
||||
|
||||
public function dumpLog(\Twig_Environment $env, $message, Data $context)
|
||||
{
|
||||
$message = twig_escape_filter($env, $message);
|
||||
|
||||
if (false === strpos($message, '{')) {
|
||||
return '<span class="dump-inline">'.$message.'</span>';
|
||||
}
|
||||
|
||||
$replacements = array();
|
||||
foreach ($context->getRawData()[1] as $k => $v) {
|
||||
$v = '{'.twig_escape_filter($env, $k).'}';
|
||||
$replacements['"'.$v.'"'] = $replacements[$v] = $this->dumpData($env, $context->seek($k));
|
||||
}
|
||||
|
||||
return '<span class="dump-inline">'.strtr($message, $replacements).'</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 3.2, to be removed in 4.0. Use the dumpData() method instead.
|
||||
*/
|
||||
public function dumpValue($value)
|
||||
{
|
||||
@trigger_error(sprintf('The %s() method is deprecated since version 3.2 and will be removed in 4.0. Use the dumpData() method instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
if (null === $this->valueExporter) {
|
||||
$this->valueExporter = new ValueExporter();
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.5.9",
|
||||
"symfony/http-kernel": "~3.1",
|
||||
"symfony/http-kernel": "~3.2",
|
||||
"symfony/polyfill-php70": "~1.0",
|
||||
"symfony/routing": "~2.8|~3.0",
|
||||
"symfony/twig-bridge": "~2.8|~3.0",
|
||||
@ -29,6 +29,9 @@
|
||||
"symfony/dependency-injection": "~2.8|~3.0",
|
||||
"symfony/stopwatch": "~2.8|~3.0"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/event-dispatcher": "<3.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Bundle\\WebProfilerBundle\\": "" },
|
||||
"exclude-from-classmap": [
|
||||
|
@ -15,6 +15,8 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
use Symfony\Component\Stopwatch\Stopwatch;
|
||||
use Symfony\Component\VarDumper\Caster\ClassStub;
|
||||
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
@ -32,6 +34,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
private $called;
|
||||
private $dispatcher;
|
||||
private $wrappedListeners;
|
||||
private $cloner;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@ -47,6 +50,9 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
$this->logger = $logger;
|
||||
$this->called = array();
|
||||
$this->wrappedListeners = array();
|
||||
if (class_exists(ClassStub::class)) {
|
||||
$this->cloner = new VarCloner();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -345,6 +351,9 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
'pretty' => $class.'::'.$listener[1],
|
||||
);
|
||||
}
|
||||
if (null !== $this->cloner) {
|
||||
$info['data'] = $this->cloner->cloneVar(array(new ClassStub($info['pretty'].'()', $listener)))->seek(0);
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
@ -95,12 +95,16 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase
|
||||
$tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
|
||||
$tdispatcher->addListener('foo', $listener = function () {});
|
||||
|
||||
$listeners = $tdispatcher->getNotCalledListeners();
|
||||
unset($listeners['foo.closure']['data']);
|
||||
$this->assertEquals(array(), $tdispatcher->getCalledListeners());
|
||||
$this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure', 'priority' => 0)), $tdispatcher->getNotCalledListeners());
|
||||
$this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure', 'priority' => 0)), $listeners);
|
||||
|
||||
$tdispatcher->dispatch('foo');
|
||||
|
||||
$this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure', 'priority' => null)), $tdispatcher->getCalledListeners());
|
||||
$listeners = $tdispatcher->getCalledListeners();
|
||||
unset($listeners['foo.closure']['data']);
|
||||
$this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure', 'priority' => null)), $listeners);
|
||||
$this->assertEquals(array(), $tdispatcher->getNotCalledListeners());
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,12 @@ use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter;
|
||||
use Symfony\Component\Validator\ConstraintViolationInterface;
|
||||
use Symfony\Component\VarDumper\Caster\Caster;
|
||||
use Symfony\Component\VarDumper\Caster\ClassStub;
|
||||
use Symfony\Component\VarDumper\Caster\StubCaster;
|
||||
use Symfony\Component\VarDumper\Cloner\Data;
|
||||
use Symfony\Component\VarDumper\Cloner\Stub;
|
||||
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link FormDataExtractorInterface}.
|
||||
@ -23,11 +29,19 @@ use Symfony\Component\Validator\ConstraintViolationInterface;
|
||||
*/
|
||||
class FormDataExtractor implements FormDataExtractorInterface
|
||||
{
|
||||
private $valueExporter;
|
||||
/**
|
||||
* @var VarCloner
|
||||
*/
|
||||
private $cloner;
|
||||
|
||||
public function __construct(ValueExporter $valueExporter = null)
|
||||
/**
|
||||
* Constructs a new data extractor.
|
||||
*/
|
||||
public function __construct(ValueExporter $valueExporter = null, $triggerDeprecationNotice = true)
|
||||
{
|
||||
$this->valueExporter = $valueExporter ?: new ValueExporter();
|
||||
if (null !== $valueExporter && $triggerDeprecationNotice) {
|
||||
@trigger_error('Passing a ValueExporter instance to '.__METHOD__.'() is deprecated in version 3.2 and will be removed in 4.0.', E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -38,18 +52,18 @@ class FormDataExtractor implements FormDataExtractorInterface
|
||||
$data = array(
|
||||
'id' => $this->buildId($form),
|
||||
'name' => $form->getName(),
|
||||
'type_class' => get_class($form->getConfig()->getType()->getInnerType()),
|
||||
'synchronized' => $this->valueExporter->exportValue($form->isSynchronized()),
|
||||
'type_class' => $this->cloneVar(new ClassStub(get_class($form->getConfig()->getType()->getInnerType()))),
|
||||
'synchronized' => $this->cloneVar($form->isSynchronized()),
|
||||
'passed_options' => array(),
|
||||
'resolved_options' => array(),
|
||||
);
|
||||
|
||||
foreach ($form->getConfig()->getAttribute('data_collector/passed_options', array()) as $option => $value) {
|
||||
$data['passed_options'][$option] = $this->valueExporter->exportValue($value);
|
||||
$data['passed_options'][$option] = $this->cloneVar($value);
|
||||
}
|
||||
|
||||
foreach ($form->getConfig()->getOptions() as $option => $value) {
|
||||
$data['resolved_options'][$option] = $this->valueExporter->exportValue($value);
|
||||
$data['resolved_options'][$option] = $this->cloneVar($value);
|
||||
}
|
||||
|
||||
ksort($data['passed_options']);
|
||||
@ -65,17 +79,17 @@ class FormDataExtractor implements FormDataExtractorInterface
|
||||
{
|
||||
$data = array(
|
||||
'default_data' => array(
|
||||
'norm' => $this->valueExporter->exportValue($form->getNormData()),
|
||||
'norm' => $this->cloneVar($form->getNormData()),
|
||||
),
|
||||
'submitted_data' => array(),
|
||||
);
|
||||
|
||||
if ($form->getData() !== $form->getNormData()) {
|
||||
$data['default_data']['model'] = $this->valueExporter->exportValue($form->getData());
|
||||
$data['default_data']['model'] = $this->cloneVar($form->getData());
|
||||
}
|
||||
|
||||
if ($form->getViewData() !== $form->getNormData()) {
|
||||
$data['default_data']['view'] = $this->valueExporter->exportValue($form->getViewData());
|
||||
$data['default_data']['view'] = $this->cloneVar($form->getViewData());
|
||||
}
|
||||
|
||||
return $data;
|
||||
@ -88,17 +102,17 @@ class FormDataExtractor implements FormDataExtractorInterface
|
||||
{
|
||||
$data = array(
|
||||
'submitted_data' => array(
|
||||
'norm' => $this->valueExporter->exportValue($form->getNormData()),
|
||||
'norm' => $this->cloneVar($form->getNormData()),
|
||||
),
|
||||
'errors' => array(),
|
||||
);
|
||||
|
||||
if ($form->getViewData() !== $form->getNormData()) {
|
||||
$data['submitted_data']['view'] = $this->valueExporter->exportValue($form->getViewData());
|
||||
$data['submitted_data']['view'] = $this->cloneVar($form->getViewData());
|
||||
}
|
||||
|
||||
if ($form->getData() !== $form->getNormData()) {
|
||||
$data['submitted_data']['model'] = $this->valueExporter->exportValue($form->getData());
|
||||
$data['submitted_data']['model'] = $this->cloneVar($form->getData());
|
||||
}
|
||||
|
||||
foreach ($form->getErrors() as $error) {
|
||||
@ -114,24 +128,14 @@ class FormDataExtractor implements FormDataExtractorInterface
|
||||
|
||||
while (null !== $cause) {
|
||||
if ($cause instanceof ConstraintViolationInterface) {
|
||||
$errorData['trace'][] = array(
|
||||
'class' => $this->valueExporter->exportValue(get_class($cause)),
|
||||
'root' => $this->valueExporter->exportValue($cause->getRoot()),
|
||||
'path' => $this->valueExporter->exportValue($cause->getPropertyPath()),
|
||||
'value' => $this->valueExporter->exportValue($cause->getInvalidValue()),
|
||||
);
|
||||
|
||||
$errorData['trace'][] = $cause;
|
||||
$cause = method_exists($cause, 'getCause') ? $cause->getCause() : null;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($cause instanceof \Exception) {
|
||||
$errorData['trace'][] = array(
|
||||
'class' => $this->valueExporter->exportValue(get_class($cause)),
|
||||
'message' => $this->valueExporter->exportValue($cause->getMessage()),
|
||||
);
|
||||
|
||||
$errorData['trace'][] = $cause;
|
||||
$cause = $cause->getPrevious();
|
||||
|
||||
continue;
|
||||
@ -142,10 +146,13 @@ class FormDataExtractor implements FormDataExtractorInterface
|
||||
break;
|
||||
}
|
||||
|
||||
if ($errorData['trace']) {
|
||||
$errorData['trace'] = $this->cloneVar($errorData['trace']);
|
||||
}
|
||||
$data['errors'][] = $errorData;
|
||||
}
|
||||
|
||||
$data['synchronized'] = $this->valueExporter->exportValue($form->isSynchronized());
|
||||
$data['synchronized'] = $this->cloneVar($form->isSynchronized());
|
||||
|
||||
return $data;
|
||||
}
|
||||
@ -168,7 +175,7 @@ class FormDataExtractor implements FormDataExtractorInterface
|
||||
}
|
||||
|
||||
foreach ($view->vars as $varName => $value) {
|
||||
$data['view_vars'][$varName] = $this->valueExporter->exportValue($value);
|
||||
$data['view_vars'][$varName] = $this->cloneVar($value);
|
||||
}
|
||||
|
||||
ksort($data['view_vars']);
|
||||
@ -193,4 +200,46 @@ class FormDataExtractor implements FormDataExtractorInterface
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the variable into a serializable Data instance.
|
||||
*
|
||||
* @param mixed $var
|
||||
*
|
||||
* @return Data
|
||||
*/
|
||||
private function cloneVar($var)
|
||||
{
|
||||
if (null === $this->cloner) {
|
||||
$this->cloner = new VarCloner();
|
||||
$this->cloner->addCasters(array(
|
||||
Stub::class => function (Stub $v, array $a, Stub $s, $isNested) {
|
||||
return $isNested ? $a : StubCaster::castStub($v, $a, $s, true);
|
||||
},
|
||||
\Exception::class => function (\Exception $e, array $a, Stub $s) {
|
||||
if (isset($a[$k = "\0Exception\0previous"])) {
|
||||
unset($a[$k]);
|
||||
++$s->cut;
|
||||
}
|
||||
|
||||
return $a;
|
||||
},
|
||||
FormInterface::class => function (FormInterface $f, array $a) {
|
||||
return array(
|
||||
Caster::PREFIX_VIRTUAL.'name' => $f->getName(),
|
||||
Caster::PREFIX_VIRTUAL.'type_class' => new ClassStub(get_class($f->getConfig()->getType()->getInnerType())),
|
||||
);
|
||||
},
|
||||
ConstraintViolationInterface::class => function (ConstraintViolationInterface $v, array $a) {
|
||||
return array(
|
||||
Caster::PREFIX_VIRTUAL.'root' => $v->getRoot(),
|
||||
Caster::PREFIX_VIRTUAL.'path' => $v->getPropertyPath(),
|
||||
Caster::PREFIX_VIRTUAL.'value' => $v->getInvalidValue(),
|
||||
);
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
return $this->cloner->cloneVar($var);
|
||||
}
|
||||
}
|
||||
|
@ -18,28 +18,17 @@ use Symfony\Component\Form\FormBuilder;
|
||||
use Symfony\Component\Form\FormError;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Form\Tests\Fixtures\FixedDataTransformer;
|
||||
use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter;
|
||||
|
||||
class FormDataExtractorTest_SimpleValueExporter extends ValueExporter
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function exportValue($value, $depth = 1, $deep = false)
|
||||
{
|
||||
return is_object($value) ? sprintf('object(%s)', get_class($value)) : var_export($value, true);
|
||||
}
|
||||
}
|
||||
use Symfony\Component\Validator\ConstraintViolation;
|
||||
use Symfony\Component\VarDumper\Cloner\Data;
|
||||
use Symfony\Component\VarDumper\Dumper\CliDumper;
|
||||
use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var FormDataExtractorTest_SimpleValueExporter
|
||||
*/
|
||||
private $valueExporter;
|
||||
use VarDumperTestTrait;
|
||||
|
||||
/**
|
||||
* @var FormDataExtractor
|
||||
@ -58,8 +47,7 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->valueExporter = new FormDataExtractorTest_SimpleValueExporter();
|
||||
$this->dataExtractor = new FormDataExtractor($this->valueExporter);
|
||||
$this->dataExtractor = new FormDataExtractor();
|
||||
$this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
|
||||
$this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface');
|
||||
}
|
||||
@ -78,11 +66,11 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertSame(array(
|
||||
'id' => 'name',
|
||||
'name' => 'name',
|
||||
'type_class' => 'stdClass',
|
||||
'type_class' => '"stdClass"',
|
||||
'synchronized' => 'true',
|
||||
'passed_options' => array(),
|
||||
'resolved_options' => array(),
|
||||
), $this->dataExtractor->extractConfiguration($form));
|
||||
), $this->inlineData($this->dataExtractor->extractConfiguration($form)));
|
||||
}
|
||||
|
||||
public function testExtractConfigurationSortsPassedOptions()
|
||||
@ -108,15 +96,15 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertSame(array(
|
||||
'id' => 'name',
|
||||
'name' => 'name',
|
||||
'type_class' => 'stdClass',
|
||||
'type_class' => '"stdClass"',
|
||||
'synchronized' => 'true',
|
||||
'passed_options' => array(
|
||||
'a' => "'bar'",
|
||||
'b' => "'foo'",
|
||||
'c' => "'baz'",
|
||||
'a' => '"bar"',
|
||||
'b' => '"foo"',
|
||||
'c' => '"baz"',
|
||||
),
|
||||
'resolved_options' => array(),
|
||||
), $this->dataExtractor->extractConfiguration($form));
|
||||
), $this->inlineData($this->dataExtractor->extractConfiguration($form)));
|
||||
}
|
||||
|
||||
public function testExtractConfigurationSortsResolvedOptions()
|
||||
@ -139,15 +127,15 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertSame(array(
|
||||
'id' => 'name',
|
||||
'name' => 'name',
|
||||
'type_class' => 'stdClass',
|
||||
'type_class' => '"stdClass"',
|
||||
'synchronized' => 'true',
|
||||
'passed_options' => array(),
|
||||
'resolved_options' => array(
|
||||
'a' => "'bar'",
|
||||
'b' => "'foo'",
|
||||
'c' => "'baz'",
|
||||
'a' => '"bar"',
|
||||
'b' => '"foo"',
|
||||
'c' => '"baz"',
|
||||
),
|
||||
), $this->dataExtractor->extractConfiguration($form));
|
||||
), $this->inlineData($this->dataExtractor->extractConfiguration($form)));
|
||||
}
|
||||
|
||||
public function testExtractConfigurationBuildsIdRecursively()
|
||||
@ -175,11 +163,11 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertSame(array(
|
||||
'id' => 'grandParent_parent_name',
|
||||
'name' => 'name',
|
||||
'type_class' => 'stdClass',
|
||||
'type_class' => '"stdClass"',
|
||||
'synchronized' => 'true',
|
||||
'passed_options' => array(),
|
||||
'resolved_options' => array(),
|
||||
), $this->dataExtractor->extractConfiguration($form));
|
||||
), $this->inlineData($this->dataExtractor->extractConfiguration($form)));
|
||||
}
|
||||
|
||||
public function testExtractDefaultData()
|
||||
@ -190,10 +178,10 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$this->assertSame(array(
|
||||
'default_data' => array(
|
||||
'norm' => "'Foobar'",
|
||||
'norm' => '"Foobar"',
|
||||
),
|
||||
'submitted_data' => array(),
|
||||
), $this->dataExtractor->extractDefaultData($form));
|
||||
), $this->inlineData($this->dataExtractor->extractDefaultData($form)));
|
||||
}
|
||||
|
||||
public function testExtractDefaultDataStoresModelDataIfDifferent()
|
||||
@ -208,11 +196,11 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$this->assertSame(array(
|
||||
'default_data' => array(
|
||||
'norm' => "'Bar'",
|
||||
'model' => "'Foo'",
|
||||
'norm' => '"Bar"',
|
||||
'model' => '"Foo"',
|
||||
),
|
||||
'submitted_data' => array(),
|
||||
), $this->dataExtractor->extractDefaultData($form));
|
||||
), $this->inlineData($this->dataExtractor->extractDefaultData($form)));
|
||||
}
|
||||
|
||||
public function testExtractDefaultDataStoresViewDataIfDifferent()
|
||||
@ -227,11 +215,11 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$this->assertSame(array(
|
||||
'default_data' => array(
|
||||
'norm' => "'Foo'",
|
||||
'view' => "'Bar'",
|
||||
'norm' => '"Foo"',
|
||||
'view' => '"Bar"',
|
||||
),
|
||||
'submitted_data' => array(),
|
||||
), $this->dataExtractor->extractDefaultData($form));
|
||||
), $this->inlineData($this->dataExtractor->extractDefaultData($form)));
|
||||
}
|
||||
|
||||
public function testExtractSubmittedData()
|
||||
@ -242,11 +230,11 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$this->assertSame(array(
|
||||
'submitted_data' => array(
|
||||
'norm' => "'Foobar'",
|
||||
'norm' => '"Foobar"',
|
||||
),
|
||||
'errors' => array(),
|
||||
'synchronized' => 'true',
|
||||
), $this->dataExtractor->extractSubmittedData($form));
|
||||
), $this->inlineData($this->dataExtractor->extractSubmittedData($form)));
|
||||
}
|
||||
|
||||
public function testExtractSubmittedDataStoresModelDataIfDifferent()
|
||||
@ -262,12 +250,12 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$this->assertSame(array(
|
||||
'submitted_data' => array(
|
||||
'norm' => "'Bar'",
|
||||
'model' => "'Foo'",
|
||||
'norm' => '"Bar"',
|
||||
'model' => '"Foo"',
|
||||
),
|
||||
'errors' => array(),
|
||||
'synchronized' => 'true',
|
||||
), $this->dataExtractor->extractSubmittedData($form));
|
||||
), $this->inlineData($this->dataExtractor->extractSubmittedData($form)));
|
||||
}
|
||||
|
||||
public function testExtractSubmittedDataStoresViewDataIfDifferent()
|
||||
@ -283,12 +271,12 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$this->assertSame(array(
|
||||
'submitted_data' => array(
|
||||
'norm' => "'Foo'",
|
||||
'view' => "'Bar'",
|
||||
'norm' => '"Foo"',
|
||||
'view' => '"Bar"',
|
||||
),
|
||||
'errors' => array(),
|
||||
'synchronized' => 'true',
|
||||
), $this->dataExtractor->extractSubmittedData($form));
|
||||
), $this->inlineData($this->dataExtractor->extractSubmittedData($form)));
|
||||
}
|
||||
|
||||
public function testExtractSubmittedDataStoresErrors()
|
||||
@ -300,13 +288,13 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$this->assertSame(array(
|
||||
'submitted_data' => array(
|
||||
'norm' => "'Foobar'",
|
||||
'norm' => '"Foobar"',
|
||||
),
|
||||
'errors' => array(
|
||||
array('message' => 'Invalid!', 'origin' => spl_object_hash($form), 'trace' => array()),
|
||||
),
|
||||
'synchronized' => 'true',
|
||||
), $this->dataExtractor->extractSubmittedData($form));
|
||||
), $this->inlineData($this->dataExtractor->extractSubmittedData($form)));
|
||||
}
|
||||
|
||||
public function testExtractSubmittedDataStoresErrorOrigin()
|
||||
@ -321,13 +309,13 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$this->assertSame(array(
|
||||
'submitted_data' => array(
|
||||
'norm' => "'Foobar'",
|
||||
'norm' => '"Foobar"',
|
||||
),
|
||||
'errors' => array(
|
||||
array('message' => 'Invalid!', 'origin' => spl_object_hash($form), 'trace' => array()),
|
||||
),
|
||||
'synchronized' => 'true',
|
||||
), $this->dataExtractor->extractSubmittedData($form));
|
||||
), $this->inlineData($this->dataExtractor->extractSubmittedData($form)));
|
||||
}
|
||||
|
||||
public function testExtractSubmittedDataStoresErrorCause()
|
||||
@ -335,24 +323,39 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
$form = $this->createBuilder('name')->getForm();
|
||||
|
||||
$exception = new \Exception();
|
||||
$violation = new ConstraintViolation('Foo', 'Foo', array(), 'Root', 'property.path', 'Invalid!', null, null, null, $exception);
|
||||
|
||||
$form->submit('Foobar');
|
||||
$form->addError(new FormError('Invalid!', null, array(), null, $exception));
|
||||
$form->addError(new FormError('Invalid!', null, array(), null, $violation));
|
||||
$origin = spl_object_hash($form);
|
||||
|
||||
$this->assertSame(array(
|
||||
'submitted_data' => array(
|
||||
'norm' => "'Foobar'",
|
||||
),
|
||||
'errors' => array(
|
||||
array('message' => 'Invalid!', 'origin' => spl_object_hash($form), 'trace' => array(
|
||||
array(
|
||||
'class' => "'Exception'",
|
||||
'message' => "''",
|
||||
),
|
||||
)),
|
||||
),
|
||||
'synchronized' => 'true',
|
||||
), $this->dataExtractor->extractSubmittedData($form));
|
||||
$this->assertDumpMatchesFormat(<<<EODUMP
|
||||
array:3 [
|
||||
"submitted_data" => array:1 [
|
||||
"norm" => ""Foobar""
|
||||
]
|
||||
"errors" => array:1 [
|
||||
0 => array:3 [
|
||||
"message" => "Invalid!"
|
||||
"origin" => "$origin"
|
||||
"trace" => """
|
||||
array:2 [\\n
|
||||
0 => Symfony\Component\Validator\ConstraintViolation {\\n
|
||||
root: "Root"\\n
|
||||
path: "property.path"\\n
|
||||
value: "Invalid!"\\n
|
||||
}\\n
|
||||
1 => Exception {%A}\\n
|
||||
]
|
||||
"""
|
||||
]
|
||||
]
|
||||
"synchronized" => "true"
|
||||
]
|
||||
EODUMP
|
||||
,
|
||||
$this->inlineData($this->dataExtractor->extractSubmittedData($form))
|
||||
);
|
||||
}
|
||||
|
||||
public function testExtractSubmittedDataRemembersIfNonSynchronized()
|
||||
@ -370,12 +373,12 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$this->assertSame(array(
|
||||
'submitted_data' => array(
|
||||
'norm' => "'Foobar'",
|
||||
'model' => 'NULL',
|
||||
'norm' => '"Foobar"',
|
||||
'model' => 'null',
|
||||
),
|
||||
'errors' => array(),
|
||||
'synchronized' => 'false',
|
||||
), $this->dataExtractor->extractSubmittedData($form));
|
||||
), $this->inlineData($this->dataExtractor->extractSubmittedData($form)));
|
||||
}
|
||||
|
||||
public function testExtractViewVariables()
|
||||
@ -394,13 +397,31 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
'id' => 'foo_bar',
|
||||
'name' => 'bar',
|
||||
'view_vars' => array(
|
||||
'a' => "'bar'",
|
||||
'b' => "'foo'",
|
||||
'c' => "'baz'",
|
||||
'id' => "'foo_bar'",
|
||||
'name' => "'bar'",
|
||||
'a' => '"bar"',
|
||||
'b' => '"foo"',
|
||||
'c' => '"baz"',
|
||||
'id' => '"foo_bar"',
|
||||
'name' => '"bar"',
|
||||
),
|
||||
), $this->dataExtractor->extractViewVariables($view));
|
||||
), $this->inlineData($this->dataExtractor->extractViewVariables($view)));
|
||||
}
|
||||
|
||||
private function inlineData(array $extraction)
|
||||
{
|
||||
$dumper = new CliDumper();
|
||||
$inlined = array();
|
||||
|
||||
foreach ($extraction as $k => $v) {
|
||||
if (is_array($v)) {
|
||||
$inlined[$k] = $this->inlineData($v);
|
||||
} elseif ($v instanceof Data) {
|
||||
$inlined[$k] = rtrim($dumper->dump($v->withRefHandles(false), true));
|
||||
} else {
|
||||
$inlined[$k] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
return $inlined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,7 +30,8 @@
|
||||
"symfony/http-foundation": "~2.8|~3.0",
|
||||
"symfony/http-kernel": "~2.8|~3.0",
|
||||
"symfony/security-csrf": "~2.8|~3.0",
|
||||
"symfony/translation": "~2.8|~3.0"
|
||||
"symfony/translation": "~2.8|~3.0",
|
||||
"symfony/var-dumper": "~3.2"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/doctrine-bridge": "<2.7",
|
||||
|
@ -1,6 +1,11 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
3.2.0
|
||||
-----
|
||||
|
||||
* deprecated `DataCollector::varToString()`, use `cloneVar()` instead
|
||||
|
||||
3.1.0
|
||||
-----
|
||||
* deprecated passing objects as URI attributes to the ESI and SSI renderers
|
||||
|
@ -12,6 +12,13 @@
|
||||
namespace Symfony\Component\HttpKernel\DataCollector;
|
||||
|
||||
use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter;
|
||||
use Symfony\Component\VarDumper\Caster\ClassStub;
|
||||
use Symfony\Component\VarDumper\Caster\LinkStub;
|
||||
use Symfony\Component\VarDumper\Caster\StubCaster;
|
||||
use Symfony\Component\VarDumper\Cloner\ClonerInterface;
|
||||
use Symfony\Component\VarDumper\Cloner\Data;
|
||||
use Symfony\Component\VarDumper\Cloner\Stub;
|
||||
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
||||
|
||||
/**
|
||||
* DataCollector.
|
||||
@ -30,6 +37,11 @@ abstract class DataCollector implements DataCollectorInterface, \Serializable
|
||||
*/
|
||||
private $valueExporter;
|
||||
|
||||
/**
|
||||
* @var ClonerInterface
|
||||
*/
|
||||
private $cloner;
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
return serialize($this->data);
|
||||
@ -40,19 +52,88 @@ abstract class DataCollector implements DataCollectorInterface, \Serializable
|
||||
$this->data = unserialize($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the variable into a serializable Data instance.
|
||||
*
|
||||
* This array can be displayed in the template using
|
||||
* the VarDumper component.
|
||||
*
|
||||
* @param mixed $var
|
||||
*
|
||||
* @return Data
|
||||
*/
|
||||
protected function cloneVar($var)
|
||||
{
|
||||
if (null === $this->cloner) {
|
||||
if (class_exists(ClassStub::class)) {
|
||||
$this->cloner = new VarCloner();
|
||||
$this->cloner->addCasters(array(
|
||||
Stub::class => function (Stub $v, array $a, Stub $s, $isNested) {
|
||||
return $isNested ? $a : StubCaster::castStub($v, $a, $s, true);
|
||||
},
|
||||
));
|
||||
} else {
|
||||
@trigger_error(sprintf('Using the %s() method without the VarDumper component is deprecated since version 3.2 and won\'t be supported in 4.0. Install symfony/var-dumper version 3.2 or above.', __METHOD__), E_USER_DEPRECATED);
|
||||
$this->cloner = false;
|
||||
}
|
||||
}
|
||||
if (false === $this->cloner) {
|
||||
if (null === $this->valueExporter) {
|
||||
$this->valueExporter = new ValueExporter();
|
||||
}
|
||||
|
||||
return $this->valueExporter->exportValue($var);
|
||||
}
|
||||
|
||||
return $this->cloner->cloneVar($this->decorateVar($var));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a PHP variable to a string.
|
||||
*
|
||||
* @param mixed $var A PHP variable
|
||||
*
|
||||
* @return string The string representation of the variable
|
||||
*
|
||||
* @deprecated Deprecated since version 3.2, to be removed in 4.0. Use cloneVar() instead.
|
||||
*/
|
||||
protected function varToString($var)
|
||||
{
|
||||
@trigger_error(sprintf('The %() method is deprecated since version 3.2 and will be removed in 4.0. Use cloneVar() instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
if (null === $this->valueExporter) {
|
||||
$this->valueExporter = new ValueExporter();
|
||||
}
|
||||
|
||||
return $this->valueExporter->exportValue($var);
|
||||
}
|
||||
|
||||
private function decorateVar($var)
|
||||
{
|
||||
if (is_array($var)) {
|
||||
if (isset($var[0], $var[1]) && is_callable($var)) {
|
||||
return ClassStub::wrapCallable($var);
|
||||
}
|
||||
foreach ($var as $k => $v) {
|
||||
if ($v !== $d = $this->decorateVar($v)) {
|
||||
$var[$k] = $d;
|
||||
}
|
||||
}
|
||||
|
||||
return $var;
|
||||
}
|
||||
if (is_string($var)) {
|
||||
if (false !== strpos($var, '\\')) {
|
||||
$c = (false !== $i = strpos($var, '::')) ? substr($var, 0, $i) : $var;
|
||||
if (class_exists($c, false) || interface_exists($c, false) || trait_exists($c, false)) {
|
||||
return new ClassStub($var);
|
||||
}
|
||||
}
|
||||
if (false !== strpos($var, DIRECTORY_SEPARATOR) && file_exists($var)) {
|
||||
return new LinkStub($var);
|
||||
}
|
||||
}
|
||||
|
||||
return $var;
|
||||
}
|
||||
}
|
||||
|
@ -23,24 +23,6 @@ use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
|
||||
*/
|
||||
class LoggerDataCollector extends DataCollector implements LateDataCollectorInterface
|
||||
{
|
||||
private $errorNames = array(
|
||||
E_DEPRECATED => 'E_DEPRECATED',
|
||||
E_USER_DEPRECATED => 'E_USER_DEPRECATED',
|
||||
E_NOTICE => 'E_NOTICE',
|
||||
E_USER_NOTICE => 'E_USER_NOTICE',
|
||||
E_STRICT => 'E_STRICT',
|
||||
E_WARNING => 'E_WARNING',
|
||||
E_USER_WARNING => 'E_USER_WARNING',
|
||||
E_COMPILE_WARNING => 'E_COMPILE_WARNING',
|
||||
E_CORE_WARNING => 'E_CORE_WARNING',
|
||||
E_USER_ERROR => 'E_USER_ERROR',
|
||||
E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
|
||||
E_COMPILE_ERROR => 'E_COMPILE_ERROR',
|
||||
E_PARSE => 'E_PARSE',
|
||||
E_ERROR => 'E_ERROR',
|
||||
E_CORE_ERROR => 'E_CORE_ERROR',
|
||||
);
|
||||
|
||||
private $logger;
|
||||
|
||||
public function __construct($logger = null)
|
||||
@ -94,6 +76,11 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
|
||||
return isset($this->data['deprecation_count']) ? $this->data['deprecation_count'] : 0;
|
||||
}
|
||||
|
||||
public function countWarnings()
|
||||
{
|
||||
return isset($this->data['warning_count']) ? $this->data['warning_count'] : 0;
|
||||
}
|
||||
|
||||
public function countScreams()
|
||||
{
|
||||
return isset($this->data['scream_count']) ? $this->data['scream_count'] : 0;
|
||||
@ -109,46 +96,31 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
|
||||
|
||||
private function sanitizeLogs($logs)
|
||||
{
|
||||
$errorContextById = array();
|
||||
$sanitizedLogs = array();
|
||||
|
||||
foreach ($logs as $log) {
|
||||
if (!$this->isSilencedOrDeprecationErrorLog($log)) {
|
||||
$log['context'] = $this->sanitizeContext($log['context']);
|
||||
$log['context'] = $log['context'] ? $this->cloneVar($log['context']) : $log['context'];
|
||||
$sanitizedLogs[] = $log;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$exception = $log['context']['exception'];
|
||||
$errorId = md5("{$exception->getSeverity()}/{$exception->getLine()}/{$exception->getFile()}".($exception instanceof \Exception ? "\0".$exception->getMessage() : ''), true);
|
||||
|
||||
$context = array(
|
||||
'type' => isset($this->errorNames[$exception->getSeverity()]) ? $this->errorNames[$exception->getSeverity()] : $exception->getSeverity(),
|
||||
'file' => $exception->getFile(),
|
||||
'line' => $exception->getLine(),
|
||||
'errorCount' => 0,
|
||||
'scream' => $exception instanceof SilencedErrorContext,
|
||||
);
|
||||
if (isset($sanitizedLogs[$errorId])) {
|
||||
++$sanitizedLogs[$errorId]['errorCount'];
|
||||
} else {
|
||||
$log['context'] = $log['context'] ? $this->cloneVar($log['context']) : $log['context'];
|
||||
|
||||
if ($exception instanceof \Exception) {
|
||||
$context['trace'] = array_map(function ($call) {
|
||||
unset($call['args']);
|
||||
$log += array(
|
||||
'errorCount' => 1,
|
||||
'scream' => $exception instanceof SilencedErrorContext,
|
||||
);
|
||||
|
||||
return $call;
|
||||
}, $exception->getTrace());
|
||||
$sanitizedLogs[$errorId] = $log;
|
||||
}
|
||||
|
||||
$errorId = md5("{$context['type']}/{$context['line']}/{$context['file']}\x00{$log['message']}", true);
|
||||
|
||||
if (!isset($errorContextById[$errorId])) {
|
||||
$errorContextById[$errorId] = $context;
|
||||
}
|
||||
|
||||
$context['errorCount'] = ++$errorContextById[$errorId]['errorCount'];
|
||||
|
||||
$log['context'] = $this->sanitizeContext($context);
|
||||
|
||||
$sanitizedLogs[$errorId] = $log;
|
||||
}
|
||||
|
||||
return array_values($sanitizedLogs);
|
||||
@ -173,48 +145,12 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
|
||||
return false;
|
||||
}
|
||||
|
||||
private function sanitizeContext($context)
|
||||
{
|
||||
if (is_array($context)) {
|
||||
foreach ($context as $key => $value) {
|
||||
$context[$key] = $this->sanitizeContext($value);
|
||||
}
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
if (is_resource($context)) {
|
||||
return sprintf('Resource(%s)', get_resource_type($context));
|
||||
}
|
||||
|
||||
if ($context instanceof \Exception) {
|
||||
$trace = array_map(function ($call) {
|
||||
unset($call['args']);
|
||||
|
||||
return $call;
|
||||
}, $context->getTrace());
|
||||
|
||||
return array(
|
||||
'class' => get_class($context),
|
||||
'message' => $context->getMessage(),
|
||||
'file' => $context->getFile(),
|
||||
'line' => $context->getLine(),
|
||||
'trace' => $trace,
|
||||
);
|
||||
}
|
||||
|
||||
if (is_object($context)) {
|
||||
return sprintf('Object(%s)', get_class($context));
|
||||
}
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
private function computeErrorsCount()
|
||||
{
|
||||
$count = array(
|
||||
'error_count' => $this->logger->countErrors(),
|
||||
'deprecation_count' => 0,
|
||||
'warning_count' => 0,
|
||||
'scream_count' => 0,
|
||||
'priorities' => array(),
|
||||
);
|
||||
@ -228,6 +164,9 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
|
||||
'name' => $log['priorityName'],
|
||||
);
|
||||
}
|
||||
if ('WARNING' === $log['priorityName']) {
|
||||
++$count['warning_count'];
|
||||
}
|
||||
|
||||
if ($this->isSilencedOrDeprecationErrorLog($log)) {
|
||||
if ($log['context']['exception'] instanceof SilencedErrorContext) {
|
||||
|
@ -52,17 +52,16 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
|
||||
|
||||
// attributes are serialized and as they can be anything, they need to be converted to strings.
|
||||
$attributes = array();
|
||||
$route = '';
|
||||
foreach ($request->attributes->all() as $key => $value) {
|
||||
if ('_route' === $key && is_object($value)) {
|
||||
$attributes[$key] = $this->varToString($value->getPath());
|
||||
} elseif ('_route_params' === $key) {
|
||||
// we need to keep route params as an array (see getRouteParams())
|
||||
foreach ($value as $k => $v) {
|
||||
$value[$k] = $this->varToString($v);
|
||||
}
|
||||
$attributes[$key] = $value;
|
||||
$attributes[$key] = $this->cloneVar($value->getPath());
|
||||
} else {
|
||||
$attributes[$key] = $this->varToString($value);
|
||||
$attributes[$key] = $this->cloneVar($value);
|
||||
}
|
||||
|
||||
if ('_route' === $key) {
|
||||
$route = is_object($value) ? $value->getPath() : $value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,12 +97,13 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
|
||||
'content_type' => $response->headers->get('Content-Type', 'text/html'),
|
||||
'status_text' => isset(Response::$statusTexts[$statusCode]) ? Response::$statusTexts[$statusCode] : '',
|
||||
'status_code' => $statusCode,
|
||||
'request_query' => $request->query->all(),
|
||||
'request_request' => $request->request->all(),
|
||||
'request_query' => array_map(array($this, 'cloneVar'), $request->query->all()),
|
||||
'request_request' => array_map(array($this, 'cloneVar'), $request->request->all()),
|
||||
'request_headers' => $request->headers->all(),
|
||||
'request_server' => $request->server->all(),
|
||||
'request_cookies' => $request->cookies->all(),
|
||||
'request_attributes' => $attributes,
|
||||
'route' => $route,
|
||||
'response_headers' => $responseHeaders,
|
||||
'session_metadata' => $sessionMetadata,
|
||||
'session_attributes' => $sessionAttributes,
|
||||
@ -247,7 +247,12 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
|
||||
*/
|
||||
public function getRoute()
|
||||
{
|
||||
return isset($this->data['request_attributes']['_route']) ? $this->data['request_attributes']['_route'] : '';
|
||||
return $this->data['route'];
|
||||
}
|
||||
|
||||
public function getIdentifier()
|
||||
{
|
||||
return $this->data['route'] ?: (is_array($this->data['controller']) ? $this->data['controller']['class'].'::'.$this->data['controller']['method'].'()' : $this->data['controller']);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -259,7 +264,7 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
|
||||
*/
|
||||
public function getRouteParams()
|
||||
{
|
||||
return isset($this->data['request_attributes']['_route_params']) ? $this->data['request_attributes']['_route_params'] : array();
|
||||
return isset($this->data['request_attributes']['_route_params']) ? $this->data['request_attributes']['_route_params'] : $this->cloneVar(array());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -11,8 +11,12 @@
|
||||
|
||||
namespace Symfony\Component\HttpKernel\DataCollector\Util;
|
||||
|
||||
@trigger_error('The '.__NAMESPACE__.'\ValueExporter class is deprecated since version 3.2 and will be removed in 4.0. Use the VarDumper component instead.', E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @deprecated since version 3.2, to be removed in 4.0. Use the VarDumper component instead.
|
||||
*/
|
||||
class ValueExporter
|
||||
{
|
||||
|
@ -82,7 +82,7 @@ class DumpDataCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
$line = __LINE__ - 1;
|
||||
$file = __FILE__;
|
||||
$xOutput = <<<EOTXT
|
||||
<pre class=sf-dump id=sf-dump data-indent-pad=" "><a href="test://{$file}:{$line}" title="{$file}"><span class=sf-dump-meta>DumpDataCollectorTest.php</span></a> on line <span class=sf-dump-meta>{$line}</span>:
|
||||
<pre class=sf-dump id=sf-dump data-indent-pad=" "><a href="test://{$file}:{$line}" title="{$file}"><span class=sf-dump-meta>DumpDataCollectorTest.php</span></a> on line <span class=sf-dump-meta>{$line}</span>:
|
||||
<span class=sf-dump-num>123</span>
|
||||
</pre>
|
||||
|
||||
|
@ -13,9 +13,12 @@ namespace Symfony\Component\HttpKernel\Tests\DataCollector;
|
||||
|
||||
use Symfony\Component\Debug\Exception\SilencedErrorContext;
|
||||
use Symfony\Component\HttpKernel\DataCollector\LoggerDataCollector;
|
||||
use Symfony\Component\VarDumper\Cloner\Data;
|
||||
|
||||
class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private static $data;
|
||||
|
||||
/**
|
||||
* @dataProvider getCollectTestData
|
||||
*/
|
||||
@ -25,19 +28,17 @@ class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
$logger->expects($this->once())->method('countErrors')->will($this->returnValue($nb));
|
||||
$logger->expects($this->exactly(2))->method('getLogs')->will($this->returnValue($logs));
|
||||
|
||||
$c = new LoggerDataCollector($logger);
|
||||
// disable cloning the context, to ease fixtures creation.
|
||||
$c = $this->getMockBuilder(LoggerDataCollector::class)
|
||||
->setMethods(array('cloneVar'))
|
||||
->setConstructorArgs(array($logger))
|
||||
->getMock();
|
||||
$c->expects($this->any())->method('cloneVar')->willReturn(self::$data);
|
||||
$c->lateCollect();
|
||||
|
||||
// Remove the trace from the real logs, to ease fixtures creation.
|
||||
$logs = array_map(function ($log) {
|
||||
unset($log['context']['trace'], $log['context']['exception']['trace']);
|
||||
|
||||
return $log;
|
||||
}, $c->getLogs());
|
||||
|
||||
$this->assertEquals('logger', $c->getName());
|
||||
$this->assertEquals($nb, $c->countErrors());
|
||||
$this->assertEquals($expectedLogs, $logs);
|
||||
$this->assertEquals($expectedLogs, $c->getLogs());
|
||||
$this->assertEquals($expectedDeprecationCount, $c->countDeprecations());
|
||||
$this->assertEquals($expectedScreamCount, $c->countScreams());
|
||||
|
||||
@ -48,6 +49,10 @@ class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function getCollectTestData()
|
||||
{
|
||||
if (null === self::$data) {
|
||||
self::$data = new Data(array());
|
||||
}
|
||||
|
||||
yield 'simple log' => array(
|
||||
1,
|
||||
array(array('message' => 'foo', 'context' => array(), 'priority' => 100, 'priorityName' => 'DEBUG')),
|
||||
@ -56,18 +61,10 @@ class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
0,
|
||||
);
|
||||
|
||||
yield 'log with a resource' => array(
|
||||
yield 'log with a context' => array(
|
||||
1,
|
||||
array(array('message' => 'foo', 'context' => array('foo' => fopen(__FILE__, 'r')), 'priority' => 100, 'priorityName' => 'DEBUG')),
|
||||
array(array('message' => 'foo', 'context' => array('foo' => 'Resource(stream)'), 'priority' => 100, 'priorityName' => 'DEBUG')),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
|
||||
yield 'log with an object' => array(
|
||||
1,
|
||||
array(array('message' => 'foo', 'context' => array('foo' => new \stdClass()), 'priority' => 100, 'priorityName' => 'DEBUG')),
|
||||
array(array('message' => 'foo', 'context' => array('foo' => 'Object(stdClass)'), 'priority' => 100, 'priorityName' => 'DEBUG')),
|
||||
array(array('message' => 'foo', 'context' => array('foo' => 'bar'), 'priority' => 100, 'priorityName' => 'DEBUG')),
|
||||
array(array('message' => 'foo', 'context' => self::$data, 'priority' => 100, 'priorityName' => 'DEBUG')),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
@ -84,9 +81,9 @@ class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
array('message' => 'foo2', 'context' => array('exception' => new \ErrorException('deprecated', 0, E_USER_DEPRECATED)), 'priority' => 100, 'priorityName' => 'DEBUG'),
|
||||
),
|
||||
array(
|
||||
array('message' => 'foo3', 'context' => array('exception' => array('file' => __FILE__, 'line' => 82, 'class' => \ErrorException::class, 'message' => 'warning')), 'priority' => 100, 'priorityName' => 'DEBUG'),
|
||||
array('message' => 'foo', 'context' => array('type' => 'E_DEPRECATED', 'file' => __FILE__, 'line' => 83, 'errorCount' => 1, 'scream' => false), 'priority' => 100, 'priorityName' => 'DEBUG'),
|
||||
array('message' => 'foo2', 'context' => array('type' => 'E_USER_DEPRECATED', 'file' => __FILE__, 'line' => 84, 'errorCount' => 1, 'scream' => false), 'priority' => 100, 'priorityName' => 'DEBUG'),
|
||||
array('message' => 'foo3', 'context' => self::$data, 'priority' => 100, 'priorityName' => 'DEBUG'),
|
||||
array('message' => 'foo', 'context' => self::$data, 'priority' => 100, 'priorityName' => 'DEBUG', 'errorCount' => 1, 'scream' => false),
|
||||
array('message' => 'foo2', 'context' => self::$data, 'priority' => 100, 'priorityName' => 'DEBUG', 'errorCount' => 1, 'scream' => false),
|
||||
),
|
||||
2,
|
||||
0,
|
||||
@ -100,8 +97,8 @@ class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
array('message' => 'foo3', 'context' => array('exception' => new SilencedErrorContext(E_USER_WARNING, __FILE__, __LINE__)), 'priority' => 100, 'priorityName' => 'DEBUG'),
|
||||
),
|
||||
array(
|
||||
array('message' => 'foo3', 'context' => array('exception' => array('file' => __FILE__, 'line' => 99, 'class' => \ErrorException::class, 'message' => 'warning')), 'priority' => 100, 'priorityName' => 'DEBUG'),
|
||||
array('message' => 'foo3', 'context' => array('type' => 'E_USER_WARNING', 'file' => __FILE__, 'line' => 100, 'errorCount' => 1, 'scream' => true), 'priority' => 100, 'priorityName' => 'DEBUG'),
|
||||
array('message' => 'foo3', 'context' => self::$data, 'priority' => 100, 'priorityName' => 'DEBUG'),
|
||||
array('message' => 'foo3', 'context' => self::$data, 'priority' => 100, 'priorityName' => 'DEBUG', 'errorCount' => 1, 'scream' => true),
|
||||
),
|
||||
0,
|
||||
1,
|
||||
|
@ -23,6 +23,8 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\VarDumper\Cloner\Data;
|
||||
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
||||
|
||||
class RequestDataCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
@ -30,8 +32,9 @@ class RequestDataCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
$c = new RequestDataCollector();
|
||||
|
||||
$c->collect($this->createRequest(), $this->createResponse());
|
||||
$c->collect($request = $this->createRequest(), $this->createResponse());
|
||||
|
||||
$cloner = new VarCloner();
|
||||
$attributes = $c->getRequestAttributes();
|
||||
|
||||
$this->assertSame('request', $c->getName());
|
||||
@ -42,12 +45,12 @@ class RequestDataCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag', $c->getRequestRequest());
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag', $c->getRequestQuery());
|
||||
$this->assertSame('html', $c->getFormat());
|
||||
$this->assertSame('foobar', $c->getRoute());
|
||||
$this->assertSame(array('name' => 'foo'), $c->getRouteParams());
|
||||
$this->assertEquals('foobar', $c->getRoute());
|
||||
$this->assertEquals($cloner->cloneVar(array('name' => 'foo')), $c->getRouteParams());
|
||||
$this->assertSame(array(), $c->getSessionAttributes());
|
||||
$this->assertSame('en', $c->getLocale());
|
||||
$this->assertRegExp('/Resource\(stream#\d+\)/', $attributes->get('resource'));
|
||||
$this->assertSame('Object(stdClass)', $attributes->get('object'));
|
||||
$this->assertEquals($cloner->cloneVar($request->attributes->get('resource')), $attributes->get('resource'));
|
||||
$this->assertEquals($cloner->cloneVar($request->attributes->get('object')), $attributes->get('object'));
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\HeaderBag', $c->getResponseHeaders());
|
||||
$this->assertSame('OK', $c->getStatusText());
|
||||
|
@ -13,6 +13,9 @@ namespace Symfony\Component\HttpKernel\Tests\DataCollector\Util;
|
||||
|
||||
use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter;
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
class ValueExporterTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
|
@ -16,6 +16,7 @@ use Symfony\Component\HttpKernel\Profiler\FileProfilerStorage;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profiler;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\VarDumper\Cloner\Data;
|
||||
|
||||
class ProfilerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
@ -35,7 +36,7 @@ class ProfilerTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$this->assertSame(204, $profile->getStatusCode());
|
||||
$this->assertSame('GET', $profile->getMethod());
|
||||
$this->assertEquals(array('foo' => 'bar'), $profiler->get('request')->getRequestQuery()->all());
|
||||
$this->assertInstanceOf(Data::class, $profiler->get('request')->getRequestQuery()->all()['foo']);
|
||||
}
|
||||
|
||||
public function testFindWorksWithDates()
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
namespace Symfony\Component\Security\Core\Authorization;
|
||||
|
||||
use Doctrine\Common\Util\ClassUtils;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
|
||||
/**
|
||||
@ -50,7 +49,7 @@ class DebugAccessDecisionManager implements AccessDecisionManagerInterface
|
||||
|
||||
$this->decisionLog[] = array(
|
||||
'attributes' => $attributes,
|
||||
'object' => $this->getStringRepresentation($object),
|
||||
'object' => $object,
|
||||
'result' => $result,
|
||||
);
|
||||
|
||||
@ -96,39 +95,4 @@ class DebugAccessDecisionManager implements AccessDecisionManagerInterface
|
||||
{
|
||||
return $this->decisionLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getStringRepresentation($object)
|
||||
{
|
||||
if (null === $object) {
|
||||
return 'NULL';
|
||||
}
|
||||
|
||||
if (!is_object($object)) {
|
||||
if (is_bool($object)) {
|
||||
return sprintf('%s (%s)', gettype($object), $object ? 'true' : 'false');
|
||||
}
|
||||
if (is_scalar($object)) {
|
||||
return sprintf('%s (%s)', gettype($object), $object);
|
||||
}
|
||||
|
||||
return gettype($object);
|
||||
}
|
||||
|
||||
$objectClass = class_exists('Doctrine\Common\Util\ClassUtils') ? ClassUtils::getClass($object) : get_class($object);
|
||||
|
||||
if (method_exists($object, 'getId')) {
|
||||
$objectAsString = sprintf('ID: %s', $object->getId());
|
||||
} elseif (method_exists($object, '__toString')) {
|
||||
$objectAsString = (string) $object;
|
||||
} else {
|
||||
$objectAsString = sprintf('object hash: %s', spl_object_hash($object));
|
||||
}
|
||||
|
||||
return sprintf('%s (%s)', $objectClass, $objectAsString);
|
||||
}
|
||||
}
|
||||
|
@ -32,12 +32,12 @@ class DebugAccessDecisionManagerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
$object = new \stdClass();
|
||||
|
||||
yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => 'NULL', 'result' => false)), null);
|
||||
yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => 'boolean (true)', 'result' => false)), true);
|
||||
yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => 'string (jolie string)', 'result' => false)), 'jolie string');
|
||||
yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => 'integer (12345)', 'result' => false)), 12345);
|
||||
yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => 'resource', 'result' => false)), fopen(__FILE__, 'r'));
|
||||
yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => 'array', 'result' => false)), array());
|
||||
yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => sprintf('stdClass (object hash: %s)', spl_object_hash($object)), 'result' => false)), $object);
|
||||
yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => null, 'result' => false)), null);
|
||||
yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => true, 'result' => false)), true);
|
||||
yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => 'jolie string', 'result' => false)), 'jolie string');
|
||||
yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => 12345, 'result' => false)), 12345);
|
||||
yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => $x = fopen(__FILE__, 'r'), 'result' => false)), $x);
|
||||
yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => $x = array(), 'result' => false)), $x);
|
||||
yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => $object, 'result' => false)), $object);
|
||||
}
|
||||
}
|
||||
|
@ -363,8 +363,7 @@ return function (root, x) {
|
||||
};
|
||||
|
||||
})(document);
|
||||
</script>
|
||||
<style>
|
||||
</script><style>
|
||||
pre.sf-dump {
|
||||
display: block;
|
||||
white-space: pre;
|
||||
|
Reference in New Issue
Block a user