Merge branch '3.4' into 4.1

* 3.4:
  Think positive
  KernelInterface can return null container
  [DI] Detect circular references with ChildDefinition parent
  [VarDumper] Fix global dump function return value for PHP7
  [Ldap] Use shut up operator on connection errors at ldap_start_tls
  Implement startTest rather than startTestSuite
  [OptionsResolver] remove dead code and useless else
  [HttpFoundation] don't override StreamedResponse::setNotModified()
  Added relevent links for parsing to the phpdoc
  Add stricter checking for valid date time string
  Fix symfony/console (optional) dependency for MonologBridge
  [Form] Fix DateTimeType html5 input format
This commit is contained in:
Nicolas Grekas 2018-09-18 14:45:12 +02:00
commit 02bbeb660f
19 changed files with 358 additions and 72 deletions

View File

@ -27,11 +27,12 @@
"symfony/var-dumper": "~3.4|~4.0"
},
"conflict": {
"symfony/console": "<3.4",
"symfony/http-foundation": "<3.4"
},
"suggest": {
"symfony/http-kernel": "For using the debugging handlers together with the response life cycle of the HTTP kernel.",
"symfony/console": "For the possibility to show log messages in console commands depending on verbosity settings. You need version ~2.3 of the console for it.",
"symfony/console": "For the possibility to show log messages in console commands depending on verbosity settings. You need version ^2.8 of the console for it.",
"symfony/event-dispatcher": "Needed when using log messages in console commands.",
"symfony/var-dumper": "For using the debugging handlers like the console handler or the log server handler."
},

View File

@ -12,8 +12,8 @@
namespace Symfony\Bridge\PhpUnit\Legacy;
/**
* CoverageListener adds `@covers <className>` on each test suite when possible
* to make the code coverage more accurate.
* CoverageListener adds `@covers <className>` on each test when possible to
* make the code coverage more accurate.
*
* @author Grégoire Pineau <lyrixx@lyrixx.info>
*

View File

@ -15,8 +15,8 @@ use PHPUnit\Framework\BaseTestListener;
use PHPUnit\Framework\Test;
/**
* CoverageListener adds `@covers <className>` on each test suite when possible
* to make the code coverage more accurate.
* CoverageListener adds `@covers <className>` on each test when possible to
* make the code coverage more accurate.
*
* @author Grégoire Pineau <lyrixx@lyrixx.info>
*

View File

@ -11,13 +11,13 @@
namespace Symfony\Bridge\PhpUnit\Legacy;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestListenerDefaultImplementation;
use PHPUnit\Framework\TestSuite;
/**
* CoverageListener adds `@covers <className>` on each test suite when possible
* to make the code coverage more accurate.
* CoverageListener adds `@covers <className>` on each test when possible to
* make the code coverage more accurate.
*
* @author Grégoire Pineau <lyrixx@lyrixx.info>
*
@ -34,8 +34,8 @@ class CoverageListenerForV7 implements TestListener
$this->trait = new CoverageListenerTrait($sutFqcnResolver, $warningOnSutNotFound);
}
public function startTestSuite(TestSuite $suite): void
public function startTest(Test $test): void
{
$this->trait->startTest($suite);
$this->trait->startTest($test);
}
}

View File

@ -1577,7 +1577,7 @@ abstract class AbstractBootstrap3LayoutTest extends AbstractLayoutTest
[@type="datetime-local"]
[@name="name"]
[@class="my&class form-control"]
[@value="2011-02-03T04:05:06Z"]
[@value="2011-02-03T04:05:06"]
'
);
}
@ -1598,7 +1598,7 @@ abstract class AbstractBootstrap3LayoutTest extends AbstractLayoutTest
[@type="datetime-local"]
[@name="name"]
[@class="my&class form-control"]
[@value="2011-02-03T04:05:06Z"]
[@value="2011-02-03T04:05:06"]
'
);
}

View File

@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\ExceptionInterface;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
/**
* This replaces all ChildDefinition instances with their equivalent fully
@ -25,6 +26,8 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException;
*/
class ResolveChildDefinitionsPass extends AbstractRecursivePass
{
private $currentPath;
protected function processValue($value, $isRoot = false)
{
if (!$value instanceof Definition) {
@ -36,6 +39,7 @@ class ResolveChildDefinitionsPass extends AbstractRecursivePass
$value = $this->container->getDefinition($this->currentId);
}
if ($value instanceof ChildDefinition) {
$this->currentPath = array();
$value = $this->resolveDefinition($value);
if ($isRoot) {
$this->container->setDefinition($this->currentId, $value);
@ -56,6 +60,8 @@ class ResolveChildDefinitionsPass extends AbstractRecursivePass
{
try {
return $this->doResolveDefinition($definition);
} catch (ServiceCircularReferenceException $e) {
throw $e;
} catch (ExceptionInterface $e) {
$r = new \ReflectionProperty($e, 'message');
$r->setAccessible(true);
@ -71,6 +77,13 @@ class ResolveChildDefinitionsPass extends AbstractRecursivePass
throw new RuntimeException(sprintf('Parent definition "%s" does not exist.', $parent));
}
$searchKey = array_search($parent, $this->currentPath);
$this->currentPath[] = $parent;
if (false !== $searchKey) {
throw new ServiceCircularReferenceException($parent, \array_slice($this->currentPath, $searchKey));
}
$parentDef = $this->container->findDefinition($parent);
if ($parentDef instanceof ChildDefinition) {
$id = $this->currentId;

View File

@ -396,4 +396,21 @@ class ResolveChildDefinitionsPassTest extends TestCase
$pass = new ResolveChildDefinitionsPass();
$pass->process($container);
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException
* @expectedExceptionMessageRegExp /^Circular reference detected for service "c", path: "c -> b -> a -> c"./
*/
public function testProcessDetectsChildDefinitionIndirectCircularReference()
{
$container = new ContainerBuilder();
$container->register('a');
$container->setDefinition('b', new ChildDefinition('a'));
$container->setDefinition('c', new ChildDefinition('b'));
$container->setDefinition('a', new ChildDefinition('c'));
$this->process($container);
}
}

View File

@ -0,0 +1,104 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
use Symfony\Component\Form\Exception\TransformationFailedException;
/**
* @author Franz Wilding <franz.wilding@me.com>
* @author Bernhard Schussek <bschussek@gmail.com>
* @author Fred Cox <mcfedr@gmail.com>
*/
class DateTimeToHtml5LocalDateTimeTransformer extends BaseDateTimeTransformer
{
const HTML5_FORMAT = 'Y-m-d\\TH:i:s';
/**
* Transforms a \DateTime into a local date and time string.
*
* According to the HTML standard, the input string of a datetime-local
* input is a RFC3339 date followed by 'T', followed by a RFC3339 time.
* https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-local-date-and-time-string
*
* @param \DateTime|\DateTimeInterface $dateTime A DateTime object
*
* @return string The formatted date
*
* @throws TransformationFailedException If the given value is not an
* instance of \DateTime or \DateTimeInterface
*/
public function transform($dateTime)
{
if (null === $dateTime) {
return '';
}
if (!$dateTime instanceof \DateTime && !$dateTime instanceof \DateTimeInterface) {
throw new TransformationFailedException('Expected a \DateTime or \DateTimeInterface.');
}
if ($this->inputTimezone !== $this->outputTimezone) {
if (!$dateTime instanceof \DateTimeImmutable) {
$dateTime = clone $dateTime;
}
$dateTime = $dateTime->setTimezone(new \DateTimeZone($this->outputTimezone));
}
return $dateTime->format(self::HTML5_FORMAT);
}
/**
* Transforms a local date and time string into a \DateTime.
*
* When transforming back to DateTime the regex is slightly laxer, taking into
* account rules for parsing a local date and time string
* https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-local-date-and-time-string
*
* @param string $dateTimeLocal Formatted string
*
* @return \DateTime Normalized date
*
* @throws TransformationFailedException If the given value is not a string,
* if the value could not be transformed
*/
public function reverseTransform($dateTimeLocal)
{
if (!\is_string($dateTimeLocal)) {
throw new TransformationFailedException('Expected a string.');
}
if ('' === $dateTimeLocal) {
return;
}
if (!preg_match('/^(\d{4})-(\d{2})-(\d{2})[T ]\d{2}:\d{2}(?::\d{2})?$/', $dateTimeLocal, $matches)) {
throw new TransformationFailedException(sprintf('The date "%s" is not a valid date.', $dateTimeLocal));
}
try {
$dateTime = new \DateTime($dateTimeLocal, new \DateTimeZone($this->outputTimezone));
} catch (\Exception $e) {
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
}
if ($this->inputTimezone !== $dateTime->getTimezone()->getName()) {
$dateTime->setTimezone(new \DateTimeZone($this->inputTimezone));
}
if (!checkdate($matches[2], $matches[3], $matches[1])) {
throw new TransformationFailedException(sprintf('The date "%s-%s-%s" is not a valid date.', $matches[1], $matches[2], $matches[3]));
}
return $dateTime;
}
}

View File

@ -16,8 +16,8 @@ use Symfony\Component\Form\Extension\Core\DataTransformer\ArrayToPartsTransforme
use Symfony\Component\Form\Extension\Core\DataTransformer\DataTransformerChain;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeImmutableToDateTimeTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToHtml5LocalDateTimeTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToRfc3339Transformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
use Symfony\Component\Form\FormBuilderInterface;
@ -34,21 +34,8 @@ class DateTimeType extends AbstractType
const DEFAULT_TIME_FORMAT = \IntlDateFormatter::MEDIUM;
/**
* This is not quite the HTML5 format yet, because ICU lacks the
* capability of parsing and generating RFC 3339 dates.
*
* For more information see:
*
* http://userguide.icu-project.org/formatparse/datetime#TOC-Date-Time-Format-Syntax
* https://www.w3.org/TR/html5/sec-forms.html#local-date-and-time-state-typedatetimelocal
* http://tools.ietf.org/html/rfc3339
*
* An ICU ticket was created:
* http://icu-project.org/trac/ticket/9421
*
* It was supposedly fixed, but is not available in all PHP installations
* yet. To temporarily circumvent this issue, DateTimeToRfc3339Transformer
* is used when the format matches this constant.
* The HTML5 datetime-local format as defined in
* http://w3c.github.io/html-reference/datatypes.html#form.data.datetime-local.
*/
const HTML5_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
@ -89,7 +76,7 @@ class DateTimeType extends AbstractType
if ('single_text' === $options['widget']) {
if (self::HTML5_FORMAT === $pattern) {
$builder->addViewTransformer(new DateTimeToRfc3339Transformer(
$builder->addViewTransformer(new DateTimeToHtml5LocalDateTimeTransformer(
$options['model_timezone'],
$options['view_timezone']
));

View File

@ -1501,7 +1501,7 @@ abstract class AbstractLayoutTest extends FormIntegrationTestCase
'/input
[@type="datetime-local"]
[@name="name"]
[@value="2011-02-03T04:05:06Z"]
[@value="2011-02-03T04:05:06"]
'
);
}
@ -1521,7 +1521,7 @@ abstract class AbstractLayoutTest extends FormIntegrationTestCase
'/input
[@type="datetime-local"]
[@name="name"]
[@value="2011-02-03T04:05:06Z"]
[@value="2011-02-03T04:05:06"]
'
);
}

View File

@ -0,0 +1,128 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToHtml5LocalDateTimeTransformer;
class DateTimeToHtml5LocalDateTimeTransformerTest extends TestCase
{
public static function assertEquals($expected, $actual, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
{
if ($expected instanceof \DateTime && $actual instanceof \DateTime) {
$expected = $expected->format('c');
$actual = $actual->format('c');
}
parent::assertEquals($expected, $actual, $message, $delta, $maxDepth, $canonicalize, $ignoreCase);
}
public function transformProvider()
{
return array(
array('UTC', 'UTC', '2010-02-03 04:05:06 UTC', '2010-02-03T04:05:06'),
array('UTC', 'UTC', null, ''),
array('America/New_York', 'Asia/Hong_Kong', '2010-02-03 04:05:06 America/New_York', '2010-02-03T17:05:06'),
array('America/New_York', 'Asia/Hong_Kong', null, ''),
array('UTC', 'Asia/Hong_Kong', '2010-02-03 04:05:06 UTC', '2010-02-03T12:05:06'),
array('America/New_York', 'UTC', '2010-02-03 04:05:06 America/New_York', '2010-02-03T09:05:06'),
);
}
public function reverseTransformProvider()
{
return array(
// format without seconds, as appears in some browsers
array('UTC', 'UTC', '2010-02-03 04:05:06 UTC', '2010-02-03T04:05:06'),
array('UTC', 'UTC', null, ''),
array('America/New_York', 'Asia/Hong_Kong', '2010-02-03 04:05:06 America/New_York', '2010-02-03T17:05:06'),
array('America/New_York', 'Asia/Hong_Kong', null, ''),
array('UTC', 'Asia/Hong_Kong', '2010-02-03 04:05:06 UTC', '2010-02-03T12:05:06'),
array('America/New_York', 'UTC', '2010-02-03 04:05:06 America/New_York', '2010-02-03T09:05:06'),
array('UTC', 'UTC', '2010-02-03 04:05:00 UTC', '2010-02-03T04:05'),
array('America/New_York', 'Asia/Hong_Kong', '2010-02-03 04:05:00 America/New_York', '2010-02-03T17:05'),
array('Europe/Amsterdam', 'Europe/Amsterdam', '2013-08-21 10:30:00 Europe/Amsterdam', '2013-08-21T10:30:00'),
);
}
/**
* @dataProvider transformProvider
*/
public function testTransform($fromTz, $toTz, $from, $to)
{
$transformer = new DateTimeToHtml5LocalDateTimeTransformer($fromTz, $toTz);
$this->assertSame($to, $transformer->transform(null !== $from ? new \DateTime($from) : null));
}
/**
* @dataProvider transformProvider
*/
public function testTransformDateTimeImmutable($fromTz, $toTz, $from, $to)
{
$transformer = new DateTimeToHtml5LocalDateTimeTransformer($fromTz, $toTz);
$this->assertSame($to, $transformer->transform(null !== $from ? new \DateTimeImmutable($from) : null));
}
/**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
*/
public function testTransformRequiresValidDateTime()
{
$transformer = new DateTimeToHtml5LocalDateTimeTransformer();
$transformer->transform('2010-01-01');
}
/**
* @dataProvider reverseTransformProvider
*/
public function testReverseTransform($toTz, $fromTz, $to, $from)
{
$transformer = new DateTimeToHtml5LocalDateTimeTransformer($toTz, $fromTz);
if (null !== $to) {
$this->assertEquals(new \DateTime($to), $transformer->reverseTransform($from));
} else {
$this->assertNull($transformer->reverseTransform($from));
}
}
/**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
*/
public function testReverseTransformRequiresString()
{
$transformer = new DateTimeToHtml5LocalDateTimeTransformer();
$transformer->reverseTransform(12345);
}
/**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
*/
public function testReverseTransformWithNonExistingDate()
{
$transformer = new DateTimeToHtml5LocalDateTimeTransformer('UTC', 'UTC');
$transformer->reverseTransform('2010-04-31T04:05');
}
/**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
*/
public function testReverseTransformExpectsValidDateString()
{
$transformer = new DateTimeToHtml5LocalDateTimeTransformer('UTC', 'UTC');
$transformer->reverseTransform('2010-2010-2010');
}
}

View File

@ -239,12 +239,12 @@ class DateTimeTypeTest extends BaseTypeTest
$outputTime = new \DateTime('2010-06-02 03:04:00 Pacific/Tahiti');
$form->submit('2010-06-02T03:04:00-10:00');
$form->submit('2010-06-02T03:04:00');
$outputTime->setTimezone(new \DateTimeZone('America/New_York'));
$this->assertEquals($outputTime, $form->getData());
$this->assertEquals('2010-06-02T03:04:00-10:00', $form->getViewData());
$this->assertEquals('2010-06-02T03:04:00', $form->getViewData());
}
public function testSubmitDifferentTimezonesDateTimeImmutable()
@ -276,10 +276,10 @@ class DateTimeTypeTest extends BaseTypeTest
'widget' => 'single_text',
));
$form->submit('2010-06-02T03:04:00Z');
$form->submit('2010-06-02T03:04:00');
$this->assertEquals('2010-06-02 03:04:00', $form->getData());
$this->assertEquals('2010-06-02T03:04:00Z', $form->getViewData());
$this->assertEquals('2010-06-02T03:04:00', $form->getViewData());
}
public function testSubmitStringSingleTextWithSeconds()
@ -292,10 +292,10 @@ class DateTimeTypeTest extends BaseTypeTest
'with_seconds' => true,
));
$form->submit('2010-06-02T03:04:05Z');
$form->submit('2010-06-02T03:04:05');
$this->assertEquals('2010-06-02 03:04:05', $form->getData());
$this->assertEquals('2010-06-02T03:04:05Z', $form->getViewData());
$this->assertEquals('2010-06-02T03:04:05', $form->getViewData());
}
public function testSubmitDifferentPattern()

View File

@ -129,6 +129,8 @@ class StreamedResponse extends Response
throw new \LogicException('The content cannot be set on a StreamedResponse instance.');
}
$this->streamed = true;
return $this;
}
@ -141,16 +143,4 @@ class StreamedResponse extends Response
{
return false;
}
/**
* {@inheritdoc}
*
* @return $this
*/
public function setNotModified()
{
$this->setCallback(function () {});
return parent::setNotModified();
}
}

View File

@ -127,7 +127,7 @@ interface KernelInterface extends HttpKernelInterface, \Serializable
/**
* Gets the current container.
*
* @return ContainerInterface A ContainerInterface instance
* @return ContainerInterface|null A ContainerInterface instance or null when the Kernel is shutdown
*/
public function getContainer();

View File

@ -137,7 +137,7 @@ class Connection extends AbstractConnection
throw new LdapException(sprintf('Could not connect to Ldap server: %s.', ldap_error($this->connection)));
}
if ('tls' === $this->config['encryption'] && false === ldap_start_tls($this->connection)) {
if ('tls' === $this->config['encryption'] && false === @ldap_start_tls($this->connection)) {
throw new LdapException(sprintf('Could not initiate TLS connection: %s.', ldap_error($this->connection)));
}
}

View File

@ -69,10 +69,10 @@ final class Lock implements LockInterface, LoggerAwareInterface
public function acquire($blocking = false)
{
try {
if (!$blocking) {
$this->store->save($this->key);
} else {
if ($blocking) {
$this->store->waitAndSave($this->key);
} else {
$this->store->save($this->key);
}
$this->dirty = true;

View File

@ -770,7 +770,9 @@ class OptionsResolver implements Options
// Don't include closures in the exception message
continue;
} elseif ($value === $allowedValue) {
}
if ($value === $allowedValue) {
$success = true;
break;
}
@ -1041,17 +1043,4 @@ class OptionsResolver implements Options
{
return (\function_exists($isFunction = 'is_'.$type) && $isFunction($value)) || $value instanceof $type;
}
private function getInvalidValues(array $arrayValues, string $type): array
{
$invalidValues = array();
foreach ($arrayValues as $key => $value) {
if (!self::isValueValidType($type, $value)) {
$invalidValues[$key] = $value;
}
}
return $invalidValues;
}
}

View File

@ -19,8 +19,8 @@ if (!function_exists('dump')) {
{
VarDumper::dump($var);
foreach ($moreVars as $var) {
VarDumper::dump($var);
foreach ($moreVars as $v) {
VarDumper::dump($v);
}
if (1 < func_num_args()) {
@ -36,8 +36,8 @@ if (!function_exists('dd')) {
{
VarDumper::dump($var);
foreach ($moreVars as $var) {
VarDumper::dump($var);
foreach ($moreVars as $v) {
VarDumper::dump($v);
}
exit(1);

View File

@ -0,0 +1,57 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Tests\Dumper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;
use Symfony\Component\VarDumper\VarDumper;
class FunctionsTest extends TestCase
{
public function testDumpReturnsFirstArg()
{
$this->setupVarDumper();
$var1 = 'a';
ob_start();
$return = dump($var1);
$out = ob_get_clean();
$this->assertEquals($var1, $return);
}
public function testDumpReturnsAllArgsInArray()
{
$this->setupVarDumper();
$var1 = 'a';
$var2 = 'b';
$var3 = 'c';
ob_start();
$return = dump($var1, $var2, $var3);
$out = ob_get_clean();
$this->assertEquals(array($var1, $var2, $var3), $return);
}
protected function setupVarDumper()
{
$cloner = new VarCloner();
$dumper = new CliDumper('php://output');
VarDumper::setHandler(function ($var) use ($cloner, $dumper) {
$dumper->dump($cloner->cloneVar($var));
});
}
}