diff --git a/UPGRADE-3.2.md b/UPGRADE-3.2.md index 28c4eecf3c..b65bdd43fc 100644 --- a/UPGRADE-3.2.md +++ b/UPGRADE-3.2.md @@ -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 --------------- diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index f5a087ab67..42f709fe7c 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -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 ---------- diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml index 9c2dfad2ee..843fc72bdb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml @@ -24,6 +24,7 @@ + false diff --git a/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php b/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php index 867a828a98..2c66e86c34 100644 --- a/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php +++ b/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php @@ -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). * diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig index c7c8147002..e857f579e3 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig +++ b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig @@ -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 %}
Logged in as {{ collector.user }} @@ -27,7 +27,7 @@ {{ is_authenticated ? 'Yes' : 'No' }}
- {% if collector.tokenClass != null %} + {% if collector.token != null %}
Token class {{ collector.tokenClass|abbr_class }} @@ -54,7 +54,7 @@ {% endblock %} {% block menu %} - + {{ include('@Security/Collector/icon.svg') }} Security @@ -63,7 +63,7 @@ {% block panel %}

Security Token

- {% if collector.tokenClass %} + {% if collector.token %}
{{ collector.user == 'anon.' ? 'Anonymous' : collector.user }} @@ -87,7 +87,7 @@ Roles - {{ 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 %}

User is not authenticated probably because they have no roles.

@@ -98,14 +98,14 @@ {% if collector.supportsRoleHierarchy %} Inherited Roles - {{ collector.inheritedRoles is empty ? 'none' : collector.inheritedRoles|yaml_encode }} + {{ collector.inheritedRoles is empty ? 'none' : profiler_dump(collector.inheritedRoles, maxDepth=1) }} {% endif %} - {% if collector.tokenClass %} + {% if collector.token %} - Token class - {{ collector.tokenClass }} + Token + {{ profiler_dump(collector.token) }} {% endif %} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php index d8435e86c5..a9b6818d64 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php @@ -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()); } diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index 4d588399df..e4d77ec239 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -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", diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig index 24a1e3fff9..401410f41c 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig @@ -75,38 +75,7 @@ {{ listener.priority|default('-') }} - - {% if listener.type == 'Closure' %} - - Closure - (there is no class or file information) - - {% elseif listener.type == 'Function' %} - - {% set link = listener.file|file_link(listener.line) %} - {% if link %} - {{ listener.function }}() - ({{ listener.file }}) - {% else %} - {{ listener.function }}() - {{ listener.file }} (line {{ listener.line }}) - {% endif %} - - {% elseif listener.type == "Method" %} - - {% set link = listener.file|file_link(listener.line) %} - {% set class_namespace = listener.class|split('\\', -1)|join('\\') %} - - {% if link %} - {{ listener.class|abbr_class|striptags }}::{{ listener.method }}() - ({{ listener.class }}) - {% else %} - {{ class_namespace }}\{{ listener.class|abbr_class|striptags }}::{{ listener.method }}() - {{ listener.file }} (line {{ listener.line }}) - {% endif %} - - {% endif %} - + {{ profiler_dump(listener.data) }} {% if loop.last %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig index c3e207bdc8..3b79db20a6 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig @@ -152,9 +152,6 @@ margin-top: -9px; margin-left: 6px; } - .form-type { - color: #999; - } .badge-error { float: right; background: #B0413E; @@ -198,7 +195,7 @@
{% 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 %}
{% else %} @@ -442,7 +439,7 @@ {% endif %} - {{ name|default('(no name)') }} {% if data.type_class is defined %}[{{ data.type_class|split('\\')|last }}]{% endif %} + {{ name|default('(no name)') }}
@@ -456,14 +453,11 @@ {% endmacro %} -{% macro form_tree_details(name, data, forms_by_hash) %} +{% macro form_tree_details(name, data, forms_by_hash, show) %} {% import _self as tree %} -
-

- {{ name|default('(no name)') }} - {% if data.type_class is defined and data.type is defined %} - [{{ data.type }}] - {% endif %} +
+

+ {{ name|default('(no name)') }} ({{ profiler_dump(data.type_class) }})

{% if data.errors is defined and data.errors|length > 0 %} @@ -496,29 +490,12 @@ {% endif %} - {% for trace in error.trace %} - {% if not loop.first %} - Caused by: - {% endif %} - - {% if trace.root is defined %} - {{ trace.class }} -
-                                    {{- trace.root -}}
-                                    {%- if trace.path is not empty -%}
-                                        {%- if trace.path|first != '[' %}.{% endif -%}
-                                        {{- trace.path -}}
-                                    {%- endif %} = {{ trace.value -}}
-                                
- {% elseif trace.message is defined %} - {{ trace.class }} -
{{ trace.message }}
- {% else %} -
{{ trace }}
- {% endif %} + {% if error.trace %} + Caused by: + {{ profiler_dump(error.trace, maxDepth=2) }} {% else %} Unknown. - {% endfor %} + {% endif %} {% endfor %} @@ -547,7 +524,7 @@ Model Format {% if data.default_data.model is defined %} - {{ data.default_data.model }} + {{ profiler_dump(data.default_data.model) }} {% else %} same as normalized format {% endif %} @@ -555,13 +532,13 @@ Normalized Format - {{ data.default_data.norm }} + {{ profiler_dump(data.default_data.norm) }} View Format {% if data.default_data.view is defined %} - {{ data.default_data.view }} + {{ profiler_dump(data.default_data.view) }} {% else %} same as normalized format {% endif %} @@ -593,7 +570,7 @@ View Format {% if data.submitted_data.view is defined %} - {{ data.submitted_data.view }} + {{ profiler_dump(data.submitted_data.view) }} {% else %} same as normalized format {% endif %} @@ -601,13 +578,13 @@ Normalized Format - {{ data.submitted_data.norm }} + {{ profiler_dump(data.submitted_data.norm) }} Model Format {% if data.submitted_data.model is defined %} - {{ data.submitted_data.model }} + {{ profiler_dump(data.submitted_data.model) }} {% else %} same as normalized format {% endif %} @@ -644,12 +621,12 @@ {% for option, value in data.passed_options %} {{ option }} - {{ value }} + {{ profiler_dump(value) }} - {% if data.resolved_options[option] is same as(value) %} + {% if data.resolved_options[option] == value %} same as passed value {% else %} - {{ data.resolved_options[option] }} + {{ profiler_dump(data.resolved_options[option]) }} {% endif %} @@ -683,7 +660,7 @@ {% for option, value in data.resolved_options %} {{ option }} - {{ value }} + {{ profiler_dump(value) }} {% endfor %} @@ -710,7 +687,7 @@ {% for variable, value in data.view_vars %} {{ variable }} - {{ value }} + {{ profiler_dump(value) }} {% endfor %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig index 6fd14ce7d3..277a046789 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig @@ -120,7 +120,7 @@

No GET parameters

{% 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 %}

POST Parameters

@@ -130,7 +130,7 @@

No POST parameters

{% 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 %}

Request Attributes

@@ -252,7 +252,7 @@ {% for child in profile.children %}

- {{- child.getcollector('request').requestattributes.get('_controller') -}} + {{- child.getcollector('request').identifier -}} (token = {{ child.token }})

diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig index 98ea6c3265..cafdeb5ac1 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig @@ -128,7 +128,7 @@ {% for child in profile.children %} {% set events = child.getcollector('time').events %}

- {{ child.getcollector('request').requestattributes.get('_controller') }} + {{ child.getcollector('request').identifier }} {{ events.__section__.duration }} ms

diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/bag.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/bag.html.twig index bfcef94403..4df5ccf1c6 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/bag.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/bag.html.twig @@ -9,7 +9,7 @@ {% for key in bag.keys|sort %} {{ key }} - {{ profiler_dump(bag.get(key)) }} + {{ profiler_dump(bag.get(key), maxDepth=maxDepth|default(0)) }} {% else %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig index eeab7f909d..7274df283e 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig @@ -889,12 +889,6 @@ 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; @@ -907,9 +901,28 @@ table.logs .sf-call-stack abbr { #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 { + 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 { border: 1px solid #DDD; diff --git a/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php b/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php index 13df2f443e..59b854dec3 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php @@ -12,31 +12,89 @@ 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 */ -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)), ); } + 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("\nvalueExporter) { $this->valueExporter = new ValueExporter(); } diff --git a/src/Symfony/Bundle/WebProfilerBundle/composer.json b/src/Symfony/Bundle/WebProfilerBundle/composer.json index 07a877d8bf..5935d6ebe6 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/composer.json +++ b/src/Symfony/Bundle/WebProfilerBundle/composer.json @@ -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", diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php index fbb28b1bc0..06f690bf4c 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php @@ -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); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php b/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php index 690e4bd1f6..6e1c43717e 100644 --- a/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php @@ -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 */ 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(<< 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; } /** diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json index 335ae06ab9..09b64b0faf 100644 --- a/src/Symfony/Component/Form/composer.json +++ b/src/Symfony/Component/Form/composer.json @@ -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", diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index 6dfd827bff..b3b70547f4 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -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 diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php index 5dca65298d..83f87d2730 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php @@ -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; + } } diff --git a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php index fd66b38cd2..f1e13ec2f4 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php @@ -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()); } /** diff --git a/src/Symfony/Component/HttpKernel/DataCollector/Util/ValueExporter.php b/src/Symfony/Component/HttpKernel/DataCollector/Util/ValueExporter.php index 034af7bb37..c505e052a2 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/Util/ValueExporter.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/Util/ValueExporter.php @@ -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 + * + * @deprecated since version 3.2, to be removed in 4.0. Use the VarDumper component instead. */ class ValueExporter { diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php index 14db808b4f..7644905770 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php @@ -82,7 +82,7 @@ class DumpDataCollectorTest extends \PHPUnit_Framework_TestCase $line = __LINE__ - 1; $file = __FILE__; $xOutput = <<DumpDataCollectorTest.php on line {$line}: +
DumpDataCollectorTest.php on line {$line}:
 123
 
diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php index eef00d4a02..4fea364ec7 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php @@ -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()); diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/Util/ValueExporterTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/Util/ValueExporterTest.php index 09810a98b1..c583918f8c 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/Util/ValueExporterTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/Util/ValueExporterTest.php @@ -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 { /** diff --git a/src/Symfony/Component/HttpKernel/Tests/Profiler/ProfilerTest.php b/src/Symfony/Component/HttpKernel/Tests/Profiler/ProfilerTest.php index fe4f430777..45c6225bd8 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Profiler/ProfilerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Profiler/ProfilerTest.php @@ -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() diff --git a/src/Symfony/Component/Security/Core/Authorization/DebugAccessDecisionManager.php b/src/Symfony/Component/Security/Core/Authorization/DebugAccessDecisionManager.php index aa15443dd0..219ca7621a 100644 --- a/src/Symfony/Component/Security/Core/Authorization/DebugAccessDecisionManager.php +++ b/src/Symfony/Component/Security/Core/Authorization/DebugAccessDecisionManager.php @@ -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); - } } diff --git a/src/Symfony/Component/Security/Core/Tests/Authorization/DebugAccessDecisionManagerTest.php b/src/Symfony/Component/Security/Core/Tests/Authorization/DebugAccessDecisionManagerTest.php index f90f7769ba..16661f7022 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authorization/DebugAccessDecisionManagerTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authorization/DebugAccessDecisionManagerTest.php @@ -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); } } diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php index a3a9e7d320..ad6fb2a21f 100644 --- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php @@ -363,8 +363,7 @@ return function (root, x) { }; })(document); - -