Merge branch '3.4' into 4.3
* 3.4: [Yaml] Implement multiline string as scalar block for tagged values [HttpFoundation] Use `Cache-Control: must-revalidate` only if explicit lifetime has been given [FrameworkBundle] Use UserInterface to @return in getUser method [CI] Replace php7.4snapshot with php7.4 in Travis configuration [ExpressionLanguage][Node][BinaryNode] Process division by zero forward caught exception [Validator][ConstraintValidator] Stop passing unnecessary timezone argument to \DateTime add tags before processing them [MonologBridge] Fix debug processor datetime type
This commit is contained in:
commit
7f2d9c2b1e
|
@ -29,7 +29,7 @@ matrix:
|
||||||
env: php_extra="7.2"
|
env: php_extra="7.2"
|
||||||
- php: 7.3
|
- php: 7.3
|
||||||
env: deps=high
|
env: deps=high
|
||||||
- php: 7.4snapshot
|
- php: 7.4
|
||||||
env: deps=low
|
env: deps=low
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ class DebugProcessor implements DebugLoggerInterface, ResetInterface
|
||||||
$hash = $this->requestStack && ($request = $this->requestStack->getCurrentRequest()) ? spl_object_hash($request) : '';
|
$hash = $this->requestStack && ($request = $this->requestStack->getCurrentRequest()) ? spl_object_hash($request) : '';
|
||||||
|
|
||||||
$this->records[$hash][] = [
|
$this->records[$hash][] = [
|
||||||
'timestamp' => $record['datetime']->getTimestamp(),
|
'timestamp' => $record['datetime'] instanceof \DateTimeInterface ? $record['datetime']->getTimestamp() : strtotime($record['datetime']),
|
||||||
'message' => $record['message'],
|
'message' => $record['message'],
|
||||||
'priority' => $record['level'],
|
'priority' => $record['level'],
|
||||||
'priorityName' => $record['level_name'],
|
'priorityName' => $record['level_name'],
|
||||||
|
|
|
@ -19,6 +19,30 @@ use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
|
||||||
class DebugProcessorTest extends TestCase
|
class DebugProcessorTest extends TestCase
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @dataProvider providerDatetimeFormatTests
|
||||||
|
*/
|
||||||
|
public function testDatetimeFormat(array $record, $expectedTimestamp)
|
||||||
|
{
|
||||||
|
$processor = new DebugProcessor();
|
||||||
|
$processor($record);
|
||||||
|
|
||||||
|
$records = $processor->getLogs();
|
||||||
|
self::assertCount(1, $records);
|
||||||
|
self::assertSame($expectedTimestamp, $records[0]['timestamp']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function providerDatetimeFormatTests(): array
|
||||||
|
{
|
||||||
|
$record = $this->getRecord();
|
||||||
|
|
||||||
|
return [
|
||||||
|
[array_merge($record, ['datetime' => new \DateTime('2019-01-01T00:01:00+00:00')]), 1546300860],
|
||||||
|
[array_merge($record, ['datetime' => '2019-01-01T00:01:00+00:00']), 1546300860],
|
||||||
|
[array_merge($record, ['datetime' => 'foo']), false],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
public function testDebugProcessor()
|
public function testDebugProcessor()
|
||||||
{
|
{
|
||||||
$processor = new DebugProcessor();
|
$processor = new DebugProcessor();
|
||||||
|
@ -75,7 +99,7 @@ class DebugProcessorTest extends TestCase
|
||||||
$debugProcessorChild->countErrors();
|
$debugProcessorChild->countErrors();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getRecord($level = Logger::WARNING, $message = 'test')
|
private function getRecord($level = Logger::WARNING, $message = 'test'): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'message' => $message,
|
'message' => $message,
|
||||||
|
|
|
@ -30,6 +30,7 @@ use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||||
use Symfony\Component\Messenger\Envelope;
|
use Symfony\Component\Messenger\Envelope;
|
||||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
use Symfony\Component\Security\Csrf\CsrfToken;
|
use Symfony\Component\Security\Csrf\CsrfToken;
|
||||||
use Symfony\Component\WebLink\EventListener\AddLinkHeaderListener;
|
use Symfony\Component\WebLink\EventListener\AddLinkHeaderListener;
|
||||||
|
|
||||||
|
@ -351,7 +352,7 @@ trait ControllerTrait
|
||||||
/**
|
/**
|
||||||
* Get a user from the Security Token Storage.
|
* Get a user from the Security Token Storage.
|
||||||
*
|
*
|
||||||
* @return object|null
|
* @return UserInterface|object|null
|
||||||
*
|
*
|
||||||
* @throws \LogicException If SecurityBundle is not available
|
* @throws \LogicException If SecurityBundle is not available
|
||||||
*
|
*
|
||||||
|
|
|
@ -32,7 +32,8 @@ class TwigBundle extends Bundle
|
||||||
{
|
{
|
||||||
parent::build($container);
|
parent::build($container);
|
||||||
|
|
||||||
$container->addCompilerPass(new ExtensionPass());
|
// ExtensionPass must be run before the FragmentRendererPass as it adds tags that are processed later
|
||||||
|
$container->addCompilerPass(new ExtensionPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 10);
|
||||||
$container->addCompilerPass(new TwigEnvironmentPass());
|
$container->addCompilerPass(new TwigEnvironmentPass());
|
||||||
$container->addCompilerPass(new TwigLoaderPass());
|
$container->addCompilerPass(new TwigLoaderPass());
|
||||||
$container->addCompilerPass(new ExceptionListenerPass());
|
$container->addCompilerPass(new ExceptionListenerPass());
|
||||||
|
|
|
@ -147,8 +147,16 @@ class BinaryNode extends Node
|
||||||
case '*':
|
case '*':
|
||||||
return $left * $right;
|
return $left * $right;
|
||||||
case '/':
|
case '/':
|
||||||
|
if (0 == $right) {
|
||||||
|
throw new \DivisionByZeroError('Division by zero');
|
||||||
|
}
|
||||||
|
|
||||||
return $left / $right;
|
return $left / $right;
|
||||||
case '%':
|
case '%':
|
||||||
|
if (0 == $right) {
|
||||||
|
throw new \DivisionByZeroError('Modulo by zero');
|
||||||
|
}
|
||||||
|
|
||||||
return $left % $right;
|
return $left % $right;
|
||||||
case 'matches':
|
case 'matches':
|
||||||
return preg_match($right, $left);
|
return preg_match($right, $left);
|
||||||
|
|
|
@ -267,13 +267,13 @@ class ResponseHeaderBag extends HeaderBag
|
||||||
*/
|
*/
|
||||||
protected function computeCacheControlValue()
|
protected function computeCacheControlValue()
|
||||||
{
|
{
|
||||||
if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) {
|
|
||||||
return 'no-cache, private';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->cacheControl) {
|
if (!$this->cacheControl) {
|
||||||
|
if ($this->has('Last-Modified') || $this->has('Expires')) {
|
||||||
|
return 'private, must-revalidate'; // allows for heuristic expiration (RFC 7234 Section 4.2.2) in the case of "Last-Modified"
|
||||||
|
}
|
||||||
|
|
||||||
// conservative by default
|
// conservative by default
|
||||||
return 'private, must-revalidate';
|
return 'no-cache, private';
|
||||||
}
|
}
|
||||||
|
|
||||||
$header = $this->getCacheControlHeader();
|
$header = $this->getCacheControlHeader();
|
||||||
|
|
|
@ -51,9 +51,9 @@ class ResponseHeaderBagTest extends TestCase
|
||||||
$this->assertTrue($bag->hasCacheControlDirective('public'));
|
$this->assertTrue($bag->hasCacheControlDirective('public'));
|
||||||
|
|
||||||
$bag = new ResponseHeaderBag(['ETag' => 'abcde']);
|
$bag = new ResponseHeaderBag(['ETag' => 'abcde']);
|
||||||
$this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
|
$this->assertEquals('no-cache, private', $bag->get('Cache-Control'));
|
||||||
$this->assertTrue($bag->hasCacheControlDirective('private'));
|
$this->assertTrue($bag->hasCacheControlDirective('private'));
|
||||||
$this->assertTrue($bag->hasCacheControlDirective('must-revalidate'));
|
$this->assertTrue($bag->hasCacheControlDirective('no-cache'));
|
||||||
$this->assertFalse($bag->hasCacheControlDirective('max-age'));
|
$this->assertFalse($bag->hasCacheControlDirective('max-age'));
|
||||||
|
|
||||||
$bag = new ResponseHeaderBag(['Expires' => 'Wed, 16 Feb 2011 14:17:43 GMT']);
|
$bag = new ResponseHeaderBag(['Expires' => 'Wed, 16 Feb 2011 14:17:43 GMT']);
|
||||||
|
|
|
@ -110,8 +110,6 @@ class ResponseCacheStrategy implements ResponseCacheStrategyInterface
|
||||||
$response->headers->set('Age', $this->age);
|
$response->headers->set('Age', $this->age);
|
||||||
|
|
||||||
if ($this->isNotCacheableResponseEmbedded) {
|
if ($this->isNotCacheableResponseEmbedded) {
|
||||||
$response->setExpires($response->getDate());
|
|
||||||
|
|
||||||
if ($this->flagDirectives['no-store']) {
|
if ($this->flagDirectives['no-store']) {
|
||||||
$response->headers->set('Cache-Control', 'no-cache, no-store, must-revalidate');
|
$response->headers->set('Cache-Control', 'no-cache, no-store, must-revalidate');
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1242,7 +1242,6 @@ class HttpCacheTest extends HttpCacheTestCase
|
||||||
$this->request('GET', '/', [], [], true);
|
$this->request('GET', '/', [], [], true);
|
||||||
$this->assertEquals('Hello World! My name is Bobby.', $this->response->getContent());
|
$this->assertEquals('Hello World! My name is Bobby.', $this->response->getContent());
|
||||||
$this->assertNull($this->response->getTtl());
|
$this->assertNull($this->response->getTtl());
|
||||||
$this->assertTrue($this->response->mustRevalidate());
|
|
||||||
$this->assertTrue($this->response->headers->hasCacheControlDirective('private'));
|
$this->assertTrue($this->response->headers->hasCacheControlDirective('private'));
|
||||||
$this->assertTrue($this->response->headers->hasCacheControlDirective('no-cache'));
|
$this->assertTrue($this->response->headers->hasCacheControlDirective('no-cache'));
|
||||||
}
|
}
|
||||||
|
@ -1273,7 +1272,6 @@ class HttpCacheTest extends HttpCacheTestCase
|
||||||
// This can neither be cached nor revalidated, so it should be private/no cache
|
// This can neither be cached nor revalidated, so it should be private/no cache
|
||||||
$this->assertEmpty($this->response->getContent());
|
$this->assertEmpty($this->response->getContent());
|
||||||
$this->assertNull($this->response->getTtl());
|
$this->assertNull($this->response->getTtl());
|
||||||
$this->assertTrue($this->response->mustRevalidate());
|
|
||||||
$this->assertTrue($this->response->headers->hasCacheControlDirective('private'));
|
$this->assertTrue($this->response->headers->hasCacheControlDirective('private'));
|
||||||
$this->assertTrue($this->response->headers->hasCacheControlDirective('no-cache'));
|
$this->assertTrue($this->response->headers->hasCacheControlDirective('no-cache'));
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,14 +174,14 @@ class PropertyAccessor implements PropertyAccessorInterface
|
||||||
$value = $zval[self::VALUE];
|
$value = $zval[self::VALUE];
|
||||||
}
|
}
|
||||||
} catch (\TypeError $e) {
|
} catch (\TypeError $e) {
|
||||||
self::throwInvalidArgumentException($e->getMessage(), $e->getTrace(), 0, $propertyPath);
|
self::throwInvalidArgumentException($e->getMessage(), $e->getTrace(), 0, $propertyPath, $e);
|
||||||
|
|
||||||
// It wasn't thrown in this class so rethrow it
|
// It wasn't thrown in this class so rethrow it
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function throwInvalidArgumentException($message, $trace, $i, $propertyPath)
|
private static function throwInvalidArgumentException($message, $trace, $i, $propertyPath, \Throwable $previous = null)
|
||||||
{
|
{
|
||||||
// the type mismatch is not caused by invalid arguments (but e.g. by an incompatible return type hint of the writer method)
|
// the type mismatch is not caused by invalid arguments (but e.g. by an incompatible return type hint of the writer method)
|
||||||
if (0 !== strpos($message, 'Argument ')) {
|
if (0 !== strpos($message, 'Argument ')) {
|
||||||
|
@ -195,7 +195,7 @@ class PropertyAccessor implements PropertyAccessorInterface
|
||||||
$type = substr($message, 2 + $j, strpos($message, ' given', $j) - $j - 2);
|
$type = substr($message, 2 + $j, strpos($message, ' given', $j) - $j - 2);
|
||||||
$message = substr($message, $pos, $j - $pos);
|
$message = substr($message, $pos, $j - $pos);
|
||||||
|
|
||||||
throw new InvalidArgumentException(sprintf('Expected argument of type "%s", "%s" given at property path "%s".', $message, 'NULL' === $type ? 'null' : $type, $propertyPath));
|
throw new InvalidArgumentException(sprintf('Expected argument of type "%s", "%s" given at property path "%s".', $message, 'NULL' === $type ? 'null' : $type, $propertyPath), 0, $previous);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,10 +93,7 @@ abstract class ConstraintValidator implements ConstraintValidatorInterface
|
||||||
// neither the native nor the stub IntlDateFormatter support
|
// neither the native nor the stub IntlDateFormatter support
|
||||||
// DateTimeImmutable as of yet
|
// DateTimeImmutable as of yet
|
||||||
if (!$value instanceof \DateTime) {
|
if (!$value instanceof \DateTime) {
|
||||||
$value = new \DateTime(
|
$value = new \DateTime($value->format('Y-m-d H:i:s.u e'));
|
||||||
$value->format('Y-m-d H:i:s.u e'),
|
|
||||||
$value->getTimezone()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $formatter->format($value);
|
return $formatter->format($value);
|
||||||
|
|
|
@ -53,10 +53,7 @@ abstract class AbstractComparisonValidatorTestCase extends ConstraintValidatorTe
|
||||||
|
|
||||||
foreach ($comparison as $i => $value) {
|
foreach ($comparison as $i => $value) {
|
||||||
if ($value instanceof \DateTime) {
|
if ($value instanceof \DateTime) {
|
||||||
$comparison[$i] = new \DateTimeImmutable(
|
$comparison[$i] = new \DateTimeImmutable($value->format('Y-m-d H:i:s.u e'));
|
||||||
$value->format('Y-m-d H:i:s.u e'),
|
|
||||||
$value->getTimezone()
|
|
||||||
);
|
|
||||||
$add = true;
|
$add = true;
|
||||||
} elseif ('DateTime' === $value) {
|
} elseif ('DateTime' === $value) {
|
||||||
$comparison[$i] = 'DateTimeImmutable';
|
$comparison[$i] = 'DateTimeImmutable';
|
||||||
|
|
|
@ -70,7 +70,7 @@ class Dumper
|
||||||
$blockIndentationIndicator = (' ' === substr($value, 0, 1)) ? (string) $this->indentation : '';
|
$blockIndentationIndicator = (' ' === substr($value, 0, 1)) ? (string) $this->indentation : '';
|
||||||
$output .= sprintf("%s%s%s |%s\n", $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', '', $blockIndentationIndicator);
|
$output .= sprintf("%s%s%s |%s\n", $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', '', $blockIndentationIndicator);
|
||||||
|
|
||||||
foreach (preg_split('/\n|\r\n/', $value) as $row) {
|
foreach (explode("\n", $value) as $row) {
|
||||||
$output .= sprintf("%s%s%s\n", $prefix, str_repeat(' ', $this->indentation), $row);
|
$output .= sprintf("%s%s%s\n", $prefix, str_repeat(' ', $this->indentation), $row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,6 +80,19 @@ class Dumper
|
||||||
if ($value instanceof TaggedValue) {
|
if ($value instanceof TaggedValue) {
|
||||||
$output .= sprintf('%s%s !%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', $value->getTag());
|
$output .= sprintf('%s%s !%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', $value->getTag());
|
||||||
|
|
||||||
|
if ($inline >= 1 && Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value->getValue()) && false !== strpos($value->getValue(), "\n") && false === strpos($value->getValue(), "\r\n")) {
|
||||||
|
// If the first line starts with a space character, the spec requires a blockIndicationIndicator
|
||||||
|
// http://www.yaml.org/spec/1.2/spec.html#id2793979
|
||||||
|
$blockIndentationIndicator = (' ' === substr($value->getValue(), 0, 1)) ? (string) $this->indentation : '';
|
||||||
|
$output .= sprintf(" |%s\n", $blockIndentationIndicator);
|
||||||
|
|
||||||
|
foreach (explode("\n", $value->getValue()) as $row) {
|
||||||
|
$output .= sprintf("%s%s%s\n", $prefix, str_repeat(' ', $this->indentation), $row);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if ($inline - 1 <= 0 || null === $value->getValue() || is_scalar($value->getValue())) {
|
if ($inline - 1 <= 0 || null === $value->getValue() || is_scalar($value->getValue())) {
|
||||||
$output .= ' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n";
|
$output .= ' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n";
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -487,6 +487,39 @@ YAML;
|
||||||
$this->assertSame($expected, $this->dumper->dump($data, 2));
|
$this->assertSame($expected, $this->dumper->dump($data, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testDumpingMultiLineStringAsScalarBlockTaggedValue()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'foo' => new TaggedValue('bar', "foo\nline with trailing spaces:\n \nbar\ninteger like line:\n123456789\nempty line:\n\nbaz"),
|
||||||
|
];
|
||||||
|
$expected = <<<YAML
|
||||||
|
foo: !bar |
|
||||||
|
foo
|
||||||
|
line with trailing spaces:
|
||||||
|
|
||||||
|
bar
|
||||||
|
integer like line:
|
||||||
|
123456789
|
||||||
|
empty line:
|
||||||
|
|
||||||
|
baz
|
||||||
|
|
||||||
|
YAML;
|
||||||
|
|
||||||
|
$this->assertSame($expected, $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDumpingInlinedMultiLineIfRnBreakLineInTaggedValue()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'data' => [
|
||||||
|
'foo' => new TaggedValue('bar', "foo\r\nline with trailing spaces:\n \nbar\ninteger like line:\n123456789\nempty line:\n\nbaz"),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assertSame(file_get_contents(__DIR__.'/Fixtures/multiple_lines_as_literal_block_for_tagged_values.yml'), $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));
|
||||||
|
}
|
||||||
|
|
||||||
public function testDumpMultiLineStringAsScalarBlock()
|
public function testDumpMultiLineStringAsScalarBlock()
|
||||||
{
|
{
|
||||||
$data = [
|
$data = [
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
data:
|
||||||
|
foo: !bar "foo\r\nline with trailing spaces:\n \nbar\ninteger like line:\n123456789\nempty line:\n\nbaz"
|
Reference in New Issue