diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 71c775edb8..99ba8e0021 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,6 @@ | Q | A | ------------- | --- -| Branch? | "master" for new features / 2.7, 2.8, 3.0 or 3.1 for fixes +| Branch? | "master" for new features / 2.7, 2.8 or 3.1 for fixes | Bug fix? | yes/no | New feature? | yes/no | BC breaks? | yes/no diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml index edc7bd412f..a562931a5e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml @@ -12,6 +12,7 @@ %form.type_extension.csrf.field_name% %validator.translation_domain% + diff --git a/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php b/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php index b2b63368e2..2a063f1bd9 100644 --- a/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php +++ b/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php @@ -138,7 +138,7 @@ class NodeBuilder implements NodeParentInterface /** * Returns the parent node. * - * @return ParentNodeDefinitionInterface The parent node + * @return ParentNodeDefinitionInterface|NodeDefinition The parent node */ public function end() { diff --git a/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php index f7f84bc071..4633dc7d2a 100644 --- a/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php @@ -107,7 +107,7 @@ abstract class NodeDefinition implements NodeParentInterface /** * Returns the parent node. * - * @return NodeParentInterface|null The builder of the parent node + * @return NodeParentInterface|NodeBuilder|NodeDefinition|null The builder of the parent node */ public function end() { diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php index bd659fdc07..4e15feb148 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php @@ -116,20 +116,29 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer return; } - $timestamp = $this->getIntlDateFormatter()->parse($value); + // date-only patterns require parsing to be done in UTC, as midnight might not exist in the local timezone due + // to DST changes + $dateOnly = $this->isPatternDateOnly(); + + $timestamp = $this->getIntlDateFormatter($dateOnly)->parse($value); if (intl_get_error_code() != 0) { throw new TransformationFailedException(intl_get_error_message()); } try { - // read timestamp into DateTime object - the formatter delivers in UTC - $dateTime = new \DateTime(sprintf('@%s', $timestamp)); + if ($dateOnly) { + // we only care about year-month-date, which has been delivered as a timestamp pointing to UTC midnight + return new \DateTime(gmdate('Y-m-d', $timestamp), new \DateTimeZone($this->inputTimezone)); + } + + // read timestamp into DateTime object - the formatter delivers a timestamp + $dateTime = new \DateTime(sprintf('@%s', $timestamp), new \DateTimeZone($this->outputTimezone)); } catch (\Exception $e) { throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); } - if ('UTC' !== $this->inputTimezone) { + if ($this->outputTimezone !== $this->inputTimezone) { $dateTime->setTimezone(new \DateTimeZone($this->inputTimezone)); } @@ -139,15 +148,17 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer /** * Returns a preconfigured IntlDateFormatter instance. * + * @param bool $ignoreTimezone Use UTC regardless of the configured timezone. + * * @return \IntlDateFormatter * * @throws TransformationFailedException in case the date formatter can not be constructed. */ - protected function getIntlDateFormatter() + protected function getIntlDateFormatter($ignoreTimezone = false) { $dateFormat = $this->dateFormat; $timeFormat = $this->timeFormat; - $timezone = $this->outputTimezone; + $timezone = $ignoreTimezone ? 'UTC' : $this->outputTimezone; $calendar = $this->calendar; $pattern = $this->pattern; @@ -162,4 +173,24 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer return $intlDateFormatter; } + + /** + * Checks if the pattern contains only a date. + * + * @param string $pattern The input pattern + * + * @return bool + */ + protected function isPatternDateOnly() + { + if (null === $this->pattern) { + return false; + } + + // strip escaped text + $pattern = preg_replace("#'(.*?)'#", '', $this->pattern); + + // check for the absence of time-related placeholders + return 0 === preg_match('#[ahHkKmsSAzZOvVxX]#', $pattern); + } } diff --git a/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php b/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php index cfb94acd1c..3bcb5470b0 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php +++ b/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php @@ -15,6 +15,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Util\ServerParams; use Symfony\Component\Security\Csrf\CsrfToken; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Component\Translation\TranslatorInterface; @@ -65,6 +66,11 @@ class CsrfValidationListener implements EventSubscriberInterface */ private $translationDomain; + /** + * @var ServerParams + */ + private $serverParams; + public static function getSubscribedEvents() { return array( @@ -72,7 +78,7 @@ class CsrfValidationListener implements EventSubscriberInterface ); } - public function __construct($fieldName, CsrfTokenManagerInterface $tokenManager, $tokenId, $errorMessage, TranslatorInterface $translator = null, $translationDomain = null) + public function __construct($fieldName, CsrfTokenManagerInterface $tokenManager, $tokenId, $errorMessage, TranslatorInterface $translator = null, $translationDomain = null, ServerParams $serverParams = null) { $this->fieldName = $fieldName; $this->tokenManager = $tokenManager; @@ -80,13 +86,15 @@ class CsrfValidationListener implements EventSubscriberInterface $this->errorMessage = $errorMessage; $this->translator = $translator; $this->translationDomain = $translationDomain; + $this->serverParams = $serverParams ?: new ServerParams(); } public function preSubmit(FormEvent $event) { $form = $event->getForm(); + $postRequestSizeExceeded = $form->getConfig()->getMethod() === 'POST' && $this->serverParams->hasPostMaxSizeBeenExceeded(); - if ($form->isRoot() && $form->getConfig()->getOption('compound')) { + if ($form->isRoot() && $form->getConfig()->getOption('compound') && !$postRequestSizeExceeded) { $data = $event->getData(); if (!isset($data[$this->fieldName]) || !$this->tokenManager->isTokenValid(new CsrfToken($this->tokenId, $data[$this->fieldName]))) { diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php index f52824a18d..d47ebd3c3c 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php +++ b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php @@ -16,6 +16,7 @@ use Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormView; use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\Util\ServerParams; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Component\Translation\TranslatorInterface; @@ -50,13 +51,19 @@ class FormTypeCsrfExtension extends AbstractTypeExtension */ private $translationDomain; - public function __construct(CsrfTokenManagerInterface $defaultTokenManager, $defaultEnabled = true, $defaultFieldName = '_token', TranslatorInterface $translator = null, $translationDomain = null) + /** + * @var ServerParams + */ + private $serverParams; + + public function __construct(CsrfTokenManagerInterface $defaultTokenManager, $defaultEnabled = true, $defaultFieldName = '_token', TranslatorInterface $translator = null, $translationDomain = null, ServerParams $serverParams = null) { $this->defaultTokenManager = $defaultTokenManager; $this->defaultEnabled = $defaultEnabled; $this->defaultFieldName = $defaultFieldName; $this->translator = $translator; $this->translationDomain = $translationDomain; + $this->serverParams = $serverParams; } /** @@ -78,7 +85,8 @@ class FormTypeCsrfExtension extends AbstractTypeExtension $options['csrf_token_id'] ?: ($builder->getName() ?: get_class($builder->getType()->getInnerType())), $options['csrf_message'], $this->translator, - $this->translationDomain + $this->translationDomain, + $this->serverParams )) ; } diff --git a/src/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php b/src/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php index 98bbd4b9ce..d1e5eece7b 100644 --- a/src/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php +++ b/src/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php @@ -73,10 +73,7 @@ class HttpFoundationRequestHandler implements RequestHandlerInterface // Mark the form with an error if the uploaded size was too large // This is done here and not in FormValidator because $_POST is // empty when that error occurs. Hence the form is never submitted. - $contentLength = $this->serverParams->getContentLength(); - $maxContentLength = $this->serverParams->getPostMaxSize(); - - if (!empty($maxContentLength) && $contentLength > $maxContentLength) { + if ($this->serverParams->hasPostMaxSizeBeenExceeded()) { // Submit the form, but don't clear the default values $form->submit(null, false); diff --git a/src/Symfony/Component/Form/NativeRequestHandler.php b/src/Symfony/Component/Form/NativeRequestHandler.php index 36a7d7cff2..5541e96ad5 100644 --- a/src/Symfony/Component/Form/NativeRequestHandler.php +++ b/src/Symfony/Component/Form/NativeRequestHandler.php @@ -81,10 +81,7 @@ class NativeRequestHandler implements RequestHandlerInterface // Mark the form with an error if the uploaded size was too large // This is done here and not in FormValidator because $_POST is // empty when that error occurs. Hence the form is never submitted. - $contentLength = $this->serverParams->getContentLength(); - $maxContentLength = $this->serverParams->getPostMaxSize(); - - if (!empty($maxContentLength) && $contentLength > $maxContentLength) { + if ($this->serverParams->hasPostMaxSizeBeenExceeded()) { // Submit the form, but don't clear the default values $form->submit(null, false); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php index ec52ab49f8..1ac2dc5ea9 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php @@ -227,6 +227,26 @@ class DateTimeToLocalizedStringTransformerTest extends DateTimeTestCase $this->assertDateTimeEquals($this->dateTime, $transformer->reverseTransform('02*2010*03 04|05|06')); } + public function testReverseTransformDateOnlyWithDstIssue() + { + $transformer = new DateTimeToLocalizedStringTransformer('Europe/Rome', 'Europe/Rome', \IntlDateFormatter::FULL, \IntlDateFormatter::FULL, \IntlDateFormatter::GREGORIAN, 'dd/MM/yyyy'); + + $this->assertDateTimeEquals( + new \DateTime('1978-05-28', new \DateTimeZone('Europe/Rome')), + $transformer->reverseTransform('28/05/1978') + ); + } + + public function testReverseTransformDateOnlyWithDstIssueAndEscapedText() + { + $transformer = new DateTimeToLocalizedStringTransformer('Europe/Rome', 'Europe/Rome', \IntlDateFormatter::FULL, \IntlDateFormatter::FULL, \IntlDateFormatter::GREGORIAN, "'day': dd 'month': MM 'year': yyyy"); + + $this->assertDateTimeEquals( + new \DateTime('1978-05-28', new \DateTimeZone('Europe/Rome')), + $transformer->reverseTransform('day: 28 month: 05 year: 1978') + ); + } + public function testReverseTransformEmpty() { $transformer = new DateTimeToLocalizedStringTransformer(); diff --git a/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php b/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php index 7206ceede7..4904b679dd 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Form\Tests\Extension\Csrf\EventListener; +use Symfony\Component\Form\Form; use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener; @@ -72,4 +73,25 @@ class CsrfValidationListenerTest extends \PHPUnit_Framework_TestCase // Validate accordingly $this->assertSame($data, $event->getData()); } + + public function testMaxPostSizeExceeded() + { + $serverParams = $this + ->getMockBuilder('\Symfony\Component\Form\Util\ServerParams') + ->disableOriginalConstructor() + ->getMock() + ; + + $serverParams + ->expects($this->once()) + ->method('hasPostMaxSizeBeenExceeded') + ->willReturn(true) + ; + + $event = new FormEvent($this->form, array('csrf' => 'token')); + $validation = new CsrfValidationListener('csrf', $this->tokenManager, 'unknown', 'Error message', null, null, $serverParams); + + $validation->preSubmit($event); + $this->assertEmpty($this->form->getErrors()); + } } diff --git a/src/Symfony/Component/Form/Util/ServerParams.php b/src/Symfony/Component/Form/Util/ServerParams.php index c4da49db84..b9f5aaff55 100644 --- a/src/Symfony/Component/Form/Util/ServerParams.php +++ b/src/Symfony/Component/Form/Util/ServerParams.php @@ -25,6 +25,19 @@ class ServerParams $this->requestStack = $requestStack; } + /** + * Returns true if the POST max size has been exceeded in the request. + * + * @return bool + */ + public function hasPostMaxSizeBeenExceeded() + { + $contentLength = $this->getContentLength(); + $maxContentLength = $this->getPostMaxSize(); + + return $maxContentLength && $contentLength > $maxContentLength; + } + /** * Returns maximum post size in bytes. * diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index e77c89bcde..f22ccf3d12 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1525,7 +1525,7 @@ class Request return stream_get_contents($this->content); } - if (null === $this->content) { + if (null === $this->content || false === $this->content) { $this->content = file_get_contents('php://input'); } diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index b795ba2488..c437e71e6d 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -1059,8 +1059,16 @@ class RequestTest extends \PHPUnit_Framework_TestCase $req->getContent($second); } + public function getContentCantBeCalledTwiceWithResourcesProvider() + { + return array( + 'Resource then fetch' => array(true, false), + 'Resource then resource' => array(true, true), + ); + } + /** - * @dataProvider getContentCantBeCalledTwiceWithResourcesProvider + * @dataProvider getContentCanBeCalledTwiceWithResourcesProvider * @requires PHP 5.6 */ public function testGetContentCanBeCalledTwiceWithResources($first, $second) @@ -1077,12 +1085,14 @@ class RequestTest extends \PHPUnit_Framework_TestCase $b = stream_get_contents($b); } - $this->assertEquals($a, $b); + $this->assertSame($a, $b); } - public function getContentCantBeCalledTwiceWithResourcesProvider() + public function getContentCanBeCalledTwiceWithResourcesProvider() { return array( + 'Fetch then fetch' => array(false, false), + 'Fetch then resource' => array(false, true), 'Resource then fetch' => array(true, false), 'Resource then resource' => array(true, true), ); diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php index ab52678c86..c5030ad920 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php @@ -201,9 +201,7 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface foreach ($this->data as $dump) { $dumper->dump($dump['data']->withMaxDepth($maxDepthLimit)->withMaxItemsPerDepth($maxItemsPerDepth)); - - rewind($data); - $dump['data'] = stream_get_contents($data); + $dump['data'] = stream_get_contents($data, -1, 0); ftruncate($data, 0); rewind($data); $dumps[] = $dump; diff --git a/src/Symfony/Component/Process/Pipes/WindowsPipes.php b/src/Symfony/Component/Process/Pipes/WindowsPipes.php index 7aa94b1ae6..a1e3115519 100644 --- a/src/Symfony/Component/Process/Pipes/WindowsPipes.php +++ b/src/Symfony/Component/Process/Pipes/WindowsPipes.php @@ -51,9 +51,10 @@ class WindowsPipes extends AbstractPipes Process::STDOUT => Process::OUT, Process::STDERR => Process::ERR, ); + $tmpCheck = false; $tmpDir = sys_get_temp_dir(); - $error = 'unknown reason'; - set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); + $lastError = 'unknown reason'; + set_error_handler(function ($type, $msg) use (&$lastError) { $lastError = $msg; }); for ($i = 0;; ++$i) { foreach ($pipes as $pipe => $name) { $file = sprintf('%s\\sf_proc_%02X.%s', $tmpDir, $i, $name); @@ -61,7 +62,11 @@ class WindowsPipes extends AbstractPipes continue 2; } $h = fopen($file, 'xb'); - if (!$h && false === strpos($error, 'File exists')) { + if (!$h) { + $error = $lastError; + if ($tmpCheck || $tmpCheck = unlink(tempnam(false, 'sf_check_'))) { + continue; + } restore_error_handler(); throw new RuntimeException(sprintf('A temporary file could not be opened to write the process output: %s', $error)); } diff --git a/src/Symfony/Component/Routing/Router.php b/src/Symfony/Component/Routing/Router.php index be4c50108e..6ac4205a59 100644 --- a/src/Symfony/Component/Routing/Router.php +++ b/src/Symfony/Component/Routing/Router.php @@ -106,9 +106,19 @@ class Router implements RouterInterface, RequestMatcherInterface * * Available options: * - * * cache_dir: The cache directory (or null to disable caching) - * * debug: Whether to enable debugging or not (false by default) - * * resource_type: Type hint for the main resource (optional) + * * cache_dir: The cache directory (or null to disable caching) + * * debug: Whether to enable debugging or not (false by default) + * * generator_class: The name of a UrlGeneratorInterface implementation + * * generator_base_class: The base class for the dumped generator class + * * generator_cache_class: The class name for the dumped generator class + * * generator_dumper_class: The name of a GeneratorDumperInterface implementation + * * matcher_class: The name of a UrlMatcherInterface implementation + * * matcher_base_class: The base class for the dumped matcher class + * * matcher_dumper_class: The class name for the dumped matcher class + * * matcher_cache_class: The name of a MatcherDumperInterface implementation + * * resource_type: Type hint for the main resource (optional) + * * strict_requirements: Configure strict requirement checking for generators + * implementing ConfigurableRequirementsInterface (default is true) * * @param array $options An array of options * diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf index 371ad9741e..f803628e6d 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf @@ -4,7 +4,7 @@ This value should be false. - Deze waarde mag niet waar zijn. + Deze waarde moet onwaar zijn. This value should be true. @@ -36,7 +36,7 @@ This field was not expected. - Dit veld was niet verwacht. + Dit veld werd niet verwacht. This field is missing. @@ -56,7 +56,7 @@ The file could not be found. - Het bestand is niet gevonden. + Het bestand kon niet gevonden worden. The file is not readable. @@ -100,7 +100,7 @@ This value is not valid. - Deze waarde is ongeldig. + Deze waarde is niet geldig. This value is not a valid time. @@ -124,7 +124,7 @@ The file could not be uploaded. - Het bestand kon niet geüpload worden. + Het bestand kon niet worden geüpload. This value should be a valid number. @@ -140,15 +140,15 @@ This value is not a valid language. - Deze waarde representeert geen geldige taal. + Deze waarde is geen geldige taal. This value is not a valid locale. - Deze waarde representeert geen geldige lokalisering. + Deze waarde is geen geldige locale. This value is not a valid country. - Deze waarde representeert geen geldig land. + Deze waarde is geen geldig land. This value is already used. @@ -184,7 +184,7 @@ The file was only partially uploaded. - Het bestand is niet geheel geüpload. + Het bestand is slechts gedeeltelijk geüpload. No file was uploaded. diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php index a3a090d086..c035a088f8 100644 --- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php @@ -54,18 +54,6 @@ class HtmlDumper extends CliDumper $this->dumpId = 'sf-dump-'.mt_rand(); } - /** - * {@inheritdoc} - */ - public function setOutput($output) - { - if ($output !== $prev = parent::setOutput($output)) { - $this->headerIsDumped = false; - } - - return $prev; - } - /** * {@inheritdoc} */ @@ -111,7 +99,7 @@ class HtmlDumper extends CliDumper */ protected function getDumpHeader() { - $this->headerIsDumped = true; + $this->headerIsDumped = null !== $this->outputStream ? $this->outputStream : $this->lineDumper; if (null !== $this->dumpHeader) { return $this->dumpHeader; @@ -433,7 +421,7 @@ EOHTML; if (-1 === $this->lastDepth) { $this->line = sprintf($this->dumpPrefix, $this->dumpId, $this->indentPad).$this->line; } - if (!$this->headerIsDumped) { + if ($this->headerIsDumped !== (null !== $this->outputStream ? $this->outputStream : $this->lineDumper)) { $this->line = $this->getDumpHeader().$this->line; } diff --git a/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php b/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php index f6ac5bac5a..12fc2adc6c 100644 --- a/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php @@ -122,8 +122,7 @@ EOTXT $data = $cloner->cloneVar($var); $out = fopen('php://memory', 'r+b'); $dumper->dump($data, $out); - rewind($out); - $out = stream_get_contents($out); + $out = stream_get_contents($out, -1, 0); $this->assertStringMatchesFormat( <<setDumpHeader(''); + $dumper->setDumpBoundaries('', ''); + $cloner = new VarCloner(); + + $dumper->dump($cloner->cloneVar(123), $out); + $dumper->dump($cloner->cloneVar(456), $out); + + $out = stream_get_contents($out, -1, 0); + + $this->assertSame(<<<'EOTXT' +123 + +456 + + +EOTXT + , $out ); }