Merge branch '5.0'
* 5.0: [Validator] Fix auto-mapping constraints should not be validated [Debug] Updated the README to deprecate the component [Cache] fix memory leak when using PhpFilesAdapter [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 Fixing bad order of operations with null coalescing operator forward caught exception [Validator][ConstraintValidator] Stop passing unnecessary timezone argument to \DateTime add tags before processing them [FrameworkBundle][ContainerLintCommand] Reinitialize bundles when the container is reprepared [Process] change the syntax of portable prepared command lines [MonologBridge] Fix debug processor datetime type
This commit is contained in:
commit
c2181b0887
@ -29,7 +29,7 @@ matrix:
|
||||
env: php_extra="7.4snapshot"
|
||||
- php: 7.3
|
||||
env: deps=high
|
||||
- php: 7.4snapshot
|
||||
- php: 7.4
|
||||
env: deps=low
|
||||
fast_finish: true
|
||||
|
||||
|
@ -21,11 +21,12 @@ use Symfony\Bridge\Doctrine\Tests\Fixtures\DoctrineLoaderNoAutoMappingEntity;
|
||||
use Symfony\Bridge\Doctrine\Tests\Fixtures\DoctrineLoaderParentEntity;
|
||||
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||
use Symfony\Bridge\Doctrine\Validator\DoctrineLoader;
|
||||
use Symfony\Component\Validator\Constraints\DisableAutoMapping;
|
||||
use Symfony\Component\Validator\Constraints\Length;
|
||||
use Symfony\Component\Validator\Mapping\AutoMappingStrategy;
|
||||
use Symfony\Component\Validator\Mapping\CascadingStrategy;
|
||||
use Symfony\Component\Validator\Mapping\ClassMetadata;
|
||||
use Symfony\Component\Validator\Mapping\Loader\AutoMappingTrait;
|
||||
use Symfony\Component\Validator\Mapping\PropertyMetadata;
|
||||
use Symfony\Component\Validator\Mapping\TraversalStrategy;
|
||||
use Symfony\Component\Validator\Tests\Fixtures\Entity;
|
||||
use Symfony\Component\Validator\Validation;
|
||||
@ -140,11 +141,12 @@ class DoctrineLoaderTest extends TestCase
|
||||
$this->assertInstanceOf(Length::class, $textFieldConstraints[0]);
|
||||
$this->assertSame(1000, $textFieldConstraints[0]->max);
|
||||
|
||||
/** @var PropertyMetadata[] $noAutoMappingMetadata */
|
||||
$noAutoMappingMetadata = $classMetadata->getPropertyMetadata('noAutoMapping');
|
||||
$this->assertCount(1, $noAutoMappingMetadata);
|
||||
$noAutoMappingConstraints = $noAutoMappingMetadata[0]->getConstraints();
|
||||
$this->assertCount(1, $noAutoMappingConstraints);
|
||||
$this->assertInstanceOf(DisableAutoMapping::class, $noAutoMappingConstraints[0]);
|
||||
$this->assertCount(0, $noAutoMappingConstraints);
|
||||
$this->assertSame(AutoMappingStrategy::DISABLED, $noAutoMappingMetadata[0]->getAutoMappingStrategy());
|
||||
}
|
||||
|
||||
public function testFieldMappingsConfiguration()
|
||||
@ -205,13 +207,15 @@ class DoctrineLoaderTest extends TestCase
|
||||
$classMetadata = $validator->getMetadataFor(new DoctrineLoaderNoAutoMappingEntity());
|
||||
|
||||
$classConstraints = $classMetadata->getConstraints();
|
||||
$this->assertCount(1, $classConstraints);
|
||||
$this->assertInstanceOf(DisableAutoMapping::class, $classConstraints[0]);
|
||||
$this->assertCount(0, $classConstraints);
|
||||
$this->assertSame(AutoMappingStrategy::DISABLED, $classMetadata->getAutoMappingStrategy());
|
||||
|
||||
$maxLengthMetadata = $classMetadata->getPropertyMetadata('maxLength');
|
||||
$this->assertEmpty($maxLengthMetadata);
|
||||
|
||||
/** @var PropertyMetadata[] $autoMappingExplicitlyEnabledMetadata */
|
||||
$autoMappingExplicitlyEnabledMetadata = $classMetadata->getPropertyMetadata('autoMappingExplicitlyEnabled');
|
||||
$this->assertCount(2, $autoMappingExplicitlyEnabledMetadata[0]->constraints);
|
||||
$this->assertCount(1, $autoMappingExplicitlyEnabledMetadata[0]->constraints);
|
||||
$this->assertSame(AutoMappingStrategy::ENABLED, $autoMappingExplicitlyEnabledMetadata[0]->getAutoMappingStrategy());
|
||||
}
|
||||
}
|
||||
|
@ -16,10 +16,9 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
||||
use Doctrine\ORM\Mapping\MappingException as OrmMappingException;
|
||||
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||
use Symfony\Component\Validator\Constraints\DisableAutoMapping;
|
||||
use Symfony\Component\Validator\Constraints\EnableAutoMapping;
|
||||
use Symfony\Component\Validator\Constraints\Length;
|
||||
use Symfony\Component\Validator\Constraints\Valid;
|
||||
use Symfony\Component\Validator\Mapping\AutoMappingStrategy;
|
||||
use Symfony\Component\Validator\Mapping\ClassMetadata;
|
||||
use Symfony\Component\Validator\Mapping\Loader\AutoMappingTrait;
|
||||
use Symfony\Component\Validator\Mapping\Loader\LoaderInterface;
|
||||
@ -76,13 +75,16 @@ final class DoctrineLoader implements LoaderInterface
|
||||
$enabledForProperty = $enabledForClass;
|
||||
$lengthConstraint = null;
|
||||
foreach ($metadata->getPropertyMetadata($mapping['fieldName']) as $propertyMetadata) {
|
||||
// Enabling or disabling auto-mapping explicitly always takes precedence
|
||||
if (AutoMappingStrategy::DISABLED === $propertyMetadata->getAutoMappingStrategy()) {
|
||||
continue 2;
|
||||
}
|
||||
if (AutoMappingStrategy::ENABLED === $propertyMetadata->getAutoMappingStrategy()) {
|
||||
$enabledForProperty = true;
|
||||
}
|
||||
|
||||
foreach ($propertyMetadata->getConstraints() as $constraint) {
|
||||
// Enabling or disabling auto-mapping explicitly always takes precedence
|
||||
if ($constraint instanceof DisableAutoMapping) {
|
||||
continue 3;
|
||||
} elseif ($constraint instanceof EnableAutoMapping) {
|
||||
$enabledForProperty = true;
|
||||
} elseif ($constraint instanceof Length) {
|
||||
if ($constraint instanceof Length) {
|
||||
$lengthConstraint = $constraint;
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@
|
||||
"symfony/proxy-manager-bridge": "^4.4|^5.0",
|
||||
"symfony/security-core": "^5.0",
|
||||
"symfony/expression-language": "^4.4|^5.0",
|
||||
"symfony/validator": "^5.0",
|
||||
"symfony/validator": "^5.0.1",
|
||||
"symfony/translation": "^4.4|^5.0",
|
||||
"symfony/var-dumper": "^4.4|^5.0",
|
||||
"doctrine/annotations": "~1.7",
|
||||
@ -55,7 +55,7 @@
|
||||
"symfony/property-info": "<5",
|
||||
"symfony/security-bundle": "<5",
|
||||
"symfony/security-core": "<5",
|
||||
"symfony/validator": "<5"
|
||||
"symfony/validator": "<5.0.1"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/form": "",
|
||||
|
@ -33,7 +33,7 @@ class DebugProcessor implements DebugLoggerInterface, ResetInterface
|
||||
$hash = $this->requestStack && ($request = $this->requestStack->getCurrentRequest()) ? spl_object_hash($request) : '';
|
||||
|
||||
$this->records[$hash][] = [
|
||||
'timestamp' => $record['datetime']->getTimestamp(),
|
||||
'timestamp' => $record['datetime'] instanceof \DateTimeInterface ? $record['datetime']->getTimestamp() : strtotime($record['datetime']),
|
||||
'message' => $record['message'],
|
||||
'priority' => $record['level'],
|
||||
'priorityName' => $record['level_name'],
|
||||
|
@ -19,6 +19,30 @@ use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
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()
|
||||
{
|
||||
$processor = new DebugProcessor();
|
||||
@ -75,7 +99,7 @@ class DebugProcessorTest extends TestCase
|
||||
$this->assertEquals(0, $debugProcessorChild->countErrors());
|
||||
}
|
||||
|
||||
private function getRecord($level = Logger::WARNING, $message = 'test')
|
||||
private function getRecord($level = Logger::WARNING, $message = 'test'): array
|
||||
{
|
||||
return [
|
||||
'message' => $message,
|
||||
|
@ -69,7 +69,11 @@ final class ContainerLintCommand extends Command
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
|
||||
if (!$kernel->isDebug() || !(new ConfigCache($kernel->getContainer()->getParameter('debug.container.dump'), true))->isFresh()) {
|
||||
$buildContainer = \Closure::bind(function () { return $this->buildContainer(); }, $kernel, \get_class($kernel));
|
||||
$buildContainer = \Closure::bind(function (): ContainerBuilder {
|
||||
$this->initializeBundles();
|
||||
|
||||
return $this->buildContainer();
|
||||
}, $kernel, \get_class($kernel));
|
||||
$container = $buildContainer();
|
||||
} else {
|
||||
(new XmlFileLoader($container = new ContainerBuilder($parameterBag = new EnvPlaceholderParameterBag()), new FileLocator()))->load($kernel->getContainer()->getParameter('debug.container.dump'));
|
||||
|
@ -351,7 +351,7 @@ abstract class AbstractController implements ServiceSubscriberInterface
|
||||
/**
|
||||
* Get a user from the Security Token Storage.
|
||||
*
|
||||
* @return mixed
|
||||
* @return UserInterface|object|null
|
||||
*
|
||||
* @throws \LogicException If SecurityBundle is not available
|
||||
*
|
||||
|
@ -31,7 +31,8 @@ class TwigBundle extends Bundle
|
||||
{
|
||||
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 TwigLoaderPass());
|
||||
$container->addCompilerPass(new RuntimeLoaderPass(), PassConfig::TYPE_BEFORE_REMOVING);
|
||||
|
@ -57,7 +57,7 @@ class PhpFilesAdapter extends AbstractAdapter implements PruneableInterface
|
||||
{
|
||||
self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time();
|
||||
|
||||
return \function_exists('opcache_invalidate') && ('cli' !== \PHP_SAPI || filter_var(ini_get('opcache.enable_cli'), FILTER_VALIDATE_BOOLEAN)) && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN);
|
||||
return \function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN) && (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) || filter_var(ini_get('opcache.enable_cli'), FILTER_VALIDATE_BOOLEAN));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -116,6 +116,8 @@ class PhpFilesAdapter extends AbstractAdapter implements PruneableInterface
|
||||
$values[$id] = null;
|
||||
} elseif (!\is_object($value)) {
|
||||
$values[$id] = $value;
|
||||
} elseif (!$value instanceof LazyValue) {
|
||||
$values[$id] = $value();
|
||||
} elseif (false === $values[$id] = include $value->file) {
|
||||
unset($values[$id], $this->values[$id]);
|
||||
$missingIds[] = $id;
|
||||
@ -137,14 +139,20 @@ class PhpFilesAdapter extends AbstractAdapter implements PruneableInterface
|
||||
try {
|
||||
$file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
|
||||
|
||||
if (\is_array($expiresAt = include $file)) {
|
||||
if (isset(self::$valuesCache[$file])) {
|
||||
[$expiresAt, $this->values[$id]] = self::$valuesCache[$file];
|
||||
} elseif (\is_array($expiresAt = include $file)) {
|
||||
if ($this->appendOnly) {
|
||||
self::$valuesCache[$file] = $expiresAt;
|
||||
}
|
||||
|
||||
[$expiresAt, $this->values[$id]] = $expiresAt;
|
||||
} elseif ($now < $expiresAt) {
|
||||
$this->values[$id] = new LazyValue($file);
|
||||
}
|
||||
|
||||
if ($now >= $expiresAt) {
|
||||
unset($this->values[$id], $missingIds[$k]);
|
||||
unset($this->values[$id], $missingIds[$k], self::$valuesCache[$file]);
|
||||
}
|
||||
} catch (\ErrorException $e) {
|
||||
unset($missingIds[$k]);
|
||||
@ -173,7 +181,13 @@ class PhpFilesAdapter extends AbstractAdapter implements PruneableInterface
|
||||
$file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
|
||||
$getExpiry = true;
|
||||
|
||||
if (\is_array($expiresAt = include $file)) {
|
||||
if (isset(self::$valuesCache[$file])) {
|
||||
[$expiresAt, $value] = self::$valuesCache[$file];
|
||||
} elseif (\is_array($expiresAt = include $file)) {
|
||||
if ($this->appendOnly) {
|
||||
self::$valuesCache[$file] = $expiresAt;
|
||||
}
|
||||
|
||||
[$expiresAt, $value] = $expiresAt;
|
||||
} elseif ($this->appendOnly) {
|
||||
$value = new LazyValue($file);
|
||||
@ -227,12 +241,14 @@ class PhpFilesAdapter extends AbstractAdapter implements PruneableInterface
|
||||
|
||||
$encodedKey = rawurlencode($key);
|
||||
|
||||
if (!$isStaticValue) {
|
||||
if ($isStaticValue) {
|
||||
$value = "return [{$expiry}, {$value}];";
|
||||
} elseif ($this->appendOnly) {
|
||||
$value = "return [{$expiry}, static function () { return {$value}; }];";
|
||||
} else {
|
||||
// We cannot use a closure here because of https://bugs.php.net/76982
|
||||
$value = str_replace('\Symfony\Component\VarExporter\Internal\\', '', $value);
|
||||
$value = "namespace Symfony\Component\VarExporter\Internal;\n\nreturn \$getExpiry ? {$expiry} : {$value};";
|
||||
} else {
|
||||
$value = "return [{$expiry}, {$value}];";
|
||||
}
|
||||
|
||||
$file = $this->files[$key] = $this->getFile($key, true);
|
||||
@ -243,6 +259,7 @@ class PhpFilesAdapter extends AbstractAdapter implements PruneableInterface
|
||||
@opcache_invalidate($file, true);
|
||||
@opcache_compile_file($file);
|
||||
}
|
||||
unset(self::$valuesCache[$file]);
|
||||
}
|
||||
|
||||
if (!$ok && !is_writable($this->directory)) {
|
||||
@ -276,6 +293,8 @@ class PhpFilesAdapter extends AbstractAdapter implements PruneableInterface
|
||||
|
||||
protected function doUnlink($file)
|
||||
{
|
||||
unset(self::$valuesCache[$file]);
|
||||
|
||||
if (self::isSupported()) {
|
||||
@opcache_invalidate($file, true);
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
<?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\Cache\Tests\Adapter;
|
||||
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Component\Cache\Adapter\PhpFilesAdapter;
|
||||
|
||||
/**
|
||||
* @group time-sensitive
|
||||
*/
|
||||
class PhpFilesAdapterAppendOnlyTest extends PhpFilesAdapterTest
|
||||
{
|
||||
protected $skippedTests = [
|
||||
'testDefaultLifeTime' => 'PhpFilesAdapter does not allow configuring a default lifetime.',
|
||||
'testExpiration' => 'PhpFilesAdapter in append-only mode does not expiration.',
|
||||
];
|
||||
|
||||
public function createCachePool(): CacheItemPoolInterface
|
||||
{
|
||||
return new PhpFilesAdapter('sf-cache', 0, null, true);
|
||||
}
|
||||
}
|
@ -147,8 +147,16 @@ class BinaryNode extends Node
|
||||
case '*':
|
||||
return $left * $right;
|
||||
case '/':
|
||||
if (0 == $right) {
|
||||
throw new \DivisionByZeroError('Division by zero');
|
||||
}
|
||||
|
||||
return $left / $right;
|
||||
case '%':
|
||||
if (0 == $right) {
|
||||
throw new \DivisionByZeroError('Modulo by zero');
|
||||
}
|
||||
|
||||
return $left % $right;
|
||||
case 'matches':
|
||||
return preg_match($right, $left);
|
||||
|
@ -262,13 +262,13 @@ class ResponseHeaderBag extends HeaderBag
|
||||
*/
|
||||
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->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
|
||||
return 'private, must-revalidate';
|
||||
return 'no-cache, private';
|
||||
}
|
||||
|
||||
$header = $this->getCacheControlHeader();
|
||||
|
@ -51,9 +51,9 @@ class ResponseHeaderBagTest extends TestCase
|
||||
$this->assertTrue($bag->hasCacheControlDirective('public'));
|
||||
|
||||
$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('must-revalidate'));
|
||||
$this->assertTrue($bag->hasCacheControlDirective('no-cache'));
|
||||
$this->assertFalse($bag->hasCacheControlDirective('max-age'));
|
||||
|
||||
$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);
|
||||
|
||||
if ($this->isNotCacheableResponseEmbedded) {
|
||||
$response->setExpires($response->getDate());
|
||||
|
||||
if ($this->flagDirectives['no-store']) {
|
||||
$response->headers->set('Cache-Control', 'no-cache, no-store, must-revalidate');
|
||||
} else {
|
||||
|
@ -1240,7 +1240,6 @@ class HttpCacheTest extends HttpCacheTestCase
|
||||
$this->request('GET', '/', [], [], true);
|
||||
$this->assertEquals('Hello World! My name is Bobby.', $this->response->getContent());
|
||||
$this->assertNull($this->response->getTtl());
|
||||
$this->assertTrue($this->response->mustRevalidate());
|
||||
$this->assertTrue($this->response->headers->hasCacheControlDirective('private'));
|
||||
$this->assertTrue($this->response->headers->hasCacheControlDirective('no-cache'));
|
||||
}
|
||||
@ -1271,7 +1270,6 @@ class HttpCacheTest extends HttpCacheTestCase
|
||||
// This can neither be cached nor revalidated, so it should be private/no cache
|
||||
$this->assertEmpty($this->response->getContent());
|
||||
$this->assertNull($this->response->getTtl());
|
||||
$this->assertTrue($this->response->mustRevalidate());
|
||||
$this->assertTrue($this->response->headers->hasCacheControlDirective('private'));
|
||||
$this->assertTrue($this->response->headers->hasCacheControlDirective('no-cache'));
|
||||
}
|
||||
|
@ -1591,12 +1591,12 @@ class Process implements \IteratorAggregate
|
||||
|
||||
private function replacePlaceholders(string $commandline, array $env)
|
||||
{
|
||||
return preg_replace_callback('/"\$([_a-zA-Z]++[_a-zA-Z0-9]*+)"/', function ($matches) use ($commandline, $env) {
|
||||
return preg_replace_callback('/"\$\{:([_a-zA-Z]++[_a-zA-Z0-9]*+)\}"/', function ($matches) use ($commandline, $env) {
|
||||
if (!isset($env[$matches[1]]) || false === $env[$matches[1]]) {
|
||||
throw new InvalidArgumentException(sprintf('Command line is missing a value for key %s: %s.', $matches[0], $commandline));
|
||||
throw new InvalidArgumentException(sprintf('Command line is missing a value for parameter "%s": %s.', $matches[1], $commandline));
|
||||
}
|
||||
|
||||
return '\\' === \DIRECTORY_SEPARATOR ? $this->escapeArgument($env[$matches[1]]) : $matches[0];
|
||||
return $this->escapeArgument($env[$matches[1]]);
|
||||
}, $commandline);
|
||||
}
|
||||
|
||||
|
@ -1463,7 +1463,7 @@ EOTXT;
|
||||
|
||||
public function testPreparedCommand()
|
||||
{
|
||||
$p = Process::fromShellCommandline('echo "$abc"DEF');
|
||||
$p = Process::fromShellCommandline('echo "${:abc}"DEF');
|
||||
$p->run(null, ['abc' => 'ABC']);
|
||||
|
||||
$this->assertSame('ABCDEF', rtrim($p->getOutput()));
|
||||
@ -1471,7 +1471,7 @@ EOTXT;
|
||||
|
||||
public function testPreparedCommandMulti()
|
||||
{
|
||||
$p = Process::fromShellCommandline('echo "$abc""$def"');
|
||||
$p = Process::fromShellCommandline('echo "${:abc}""${:def}"');
|
||||
$p->run(null, ['abc' => 'ABC', 'def' => 'DEF']);
|
||||
|
||||
$this->assertSame('ABCDEF', rtrim($p->getOutput()));
|
||||
@ -1479,7 +1479,7 @@ EOTXT;
|
||||
|
||||
public function testPreparedCommandWithQuoteInIt()
|
||||
{
|
||||
$p = Process::fromShellCommandline('php -r "$code" "$def"');
|
||||
$p = Process::fromShellCommandline('php -r "${:code}" "${:def}"');
|
||||
$p->run(null, ['code' => 'echo $argv[1];', 'def' => '"DEF"']);
|
||||
|
||||
$this->assertSame('"DEF"', rtrim($p->getOutput()));
|
||||
@ -1488,16 +1488,16 @@ EOTXT;
|
||||
public function testPreparedCommandWithMissingValue()
|
||||
{
|
||||
$this->expectException('Symfony\Component\Process\Exception\InvalidArgumentException');
|
||||
$this->expectExceptionMessage('Command line is missing a value for key "$abc": echo "$abc".');
|
||||
$p = Process::fromShellCommandline('echo "$abc"');
|
||||
$this->expectExceptionMessage('Command line is missing a value for parameter "abc": echo "${:abc}".');
|
||||
$p = Process::fromShellCommandline('echo "${:abc}"');
|
||||
$p->run(null, ['bcd' => 'BCD']);
|
||||
}
|
||||
|
||||
public function testPreparedCommandWithNoValues()
|
||||
{
|
||||
$this->expectException('Symfony\Component\Process\Exception\InvalidArgumentException');
|
||||
$this->expectExceptionMessage('Command line is missing a value for key "$abc": echo "$abc".');
|
||||
$p = Process::fromShellCommandline('echo "$abc"');
|
||||
$this->expectExceptionMessage('Command line is missing a value for parameter "abc": echo "${:abc}".');
|
||||
$p = Process::fromShellCommandline('echo "${:abc}"');
|
||||
$p->run(null, []);
|
||||
}
|
||||
|
||||
|
@ -174,14 +174,14 @@ class PropertyAccessor implements PropertyAccessorInterface
|
||||
$value = $zval[self::VALUE];
|
||||
}
|
||||
} 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
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
private static function throwInvalidArgumentException(string $message, array $trace, int $i, string $propertyPath): void
|
||||
private static function throwInvalidArgumentException(string $message, array $trace, int $i, string $propertyPath, \Throwable $previous = null): void
|
||||
{
|
||||
// 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 ')) {
|
||||
@ -195,7 +195,7 @@ class PropertyAccessor implements PropertyAccessorInterface
|
||||
$type = substr($message, 2 + $j, strpos($message, ' given', $j) - $j - 2);
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ class ProblemNormalizer implements NormalizerInterface, CacheableSupportsMethodI
|
||||
public function normalize($exception, string $format = null, array $context = [])
|
||||
{
|
||||
$context += $this->defaultContext;
|
||||
$debug = $this->debug && $context['debug'] ?? true;
|
||||
$debug = $this->debug && ($context['debug'] ?? true);
|
||||
|
||||
$data = [
|
||||
'type' => $context['type'],
|
||||
|
@ -93,10 +93,7 @@ abstract class ConstraintValidator implements ConstraintValidatorInterface
|
||||
// neither the native nor the stub IntlDateFormatter support
|
||||
// DateTimeImmutable as of yet
|
||||
if (!$value instanceof \DateTime) {
|
||||
$value = new \DateTime(
|
||||
$value->format('Y-m-d H:i:s.u e'),
|
||||
$value->getTimezone()
|
||||
);
|
||||
$value = new \DateTime($value->format('Y-m-d H:i:s.u e'));
|
||||
}
|
||||
|
||||
return $formatter->format($value);
|
||||
|
@ -0,0 +1,42 @@
|
||||
<?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\Validator\Mapping;
|
||||
|
||||
/**
|
||||
* Specifies how the auto-mapping feature should behave.
|
||||
*
|
||||
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
|
||||
*/
|
||||
final class AutoMappingStrategy
|
||||
{
|
||||
/**
|
||||
* Nothing explicitly set, rely on auto-mapping configured regex.
|
||||
*/
|
||||
public const NONE = 0;
|
||||
|
||||
/**
|
||||
* Explicitly enabled.
|
||||
*/
|
||||
public const ENABLED = 1;
|
||||
|
||||
/**
|
||||
* Explicitly disabled.
|
||||
*/
|
||||
public const DISABLED = 2;
|
||||
|
||||
/**
|
||||
* Not instantiable.
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
}
|
@ -12,6 +12,13 @@
|
||||
namespace Symfony\Component\Validator\Mapping;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
use Symfony\Component\Validator\Constraints\DisableAutoMapping;
|
||||
use Symfony\Component\Validator\Constraints\EnableAutoMapping;
|
||||
use Symfony\Component\Validator\Constraints\Length;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
>>>>>>> 4.4
|
||||
use Symfony\Component\Validator\Constraints\Traverse;
|
||||
use Symfony\Component\Validator\Constraints\Valid;
|
||||
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
|
||||
@ -73,6 +80,19 @@ class GenericMetadata implements MetadataInterface
|
||||
*/
|
||||
public $traversalStrategy = TraversalStrategy::NONE;
|
||||
|
||||
/**
|
||||
* Is auto-mapping enabled?
|
||||
*
|
||||
* @var int
|
||||
*
|
||||
* @see AutoMappingStrategy
|
||||
*
|
||||
* @internal This property is public in order to reduce the size of the
|
||||
* class' serialized representation. Do not access it. Use
|
||||
* {@link getAutoMappingStrategy()} instead.
|
||||
*/
|
||||
public $autoMappingStrategy = AutoMappingStrategy::NONE;
|
||||
|
||||
/**
|
||||
* Returns the names of the properties that should be serialized.
|
||||
*
|
||||
@ -85,6 +105,7 @@ class GenericMetadata implements MetadataInterface
|
||||
'constraintsByGroup',
|
||||
'cascadingStrategy',
|
||||
'traversalStrategy',
|
||||
'autoMappingStrategy',
|
||||
];
|
||||
}
|
||||
|
||||
@ -137,6 +158,13 @@ class GenericMetadata implements MetadataInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($constraint instanceof DisableAutoMapping || $constraint instanceof EnableAutoMapping) {
|
||||
$this->autoMappingStrategy = $constraint instanceof EnableAutoMapping ? AutoMappingStrategy::ENABLED : AutoMappingStrategy::DISABLED;
|
||||
|
||||
// The constraint is not added
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->constraints[] = $constraint;
|
||||
|
||||
foreach ($constraint->groups as $group) {
|
||||
@ -205,4 +233,12 @@ class GenericMetadata implements MetadataInterface
|
||||
{
|
||||
return $this->traversalStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see AutoMappingStrategy
|
||||
*/
|
||||
public function getAutoMappingStrategy(): int
|
||||
{
|
||||
return $this->autoMappingStrategy;
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +11,7 @@
|
||||
|
||||
namespace Symfony\Component\Validator\Mapping\Loader;
|
||||
|
||||
use Symfony\Component\Validator\Constraints\DisableAutoMapping;
|
||||
use Symfony\Component\Validator\Constraints\EnableAutoMapping;
|
||||
use Symfony\Component\Validator\Mapping\AutoMappingStrategy;
|
||||
use Symfony\Component\Validator\Mapping\ClassMetadata;
|
||||
|
||||
/**
|
||||
@ -25,14 +24,8 @@ trait AutoMappingTrait
|
||||
private function isAutoMappingEnabledForClass(ClassMetadata $metadata, string $classValidatorRegexp = null): bool
|
||||
{
|
||||
// Check if AutoMapping constraint is set first
|
||||
foreach ($metadata->getConstraints() as $constraint) {
|
||||
if ($constraint instanceof DisableAutoMapping) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($constraint instanceof EnableAutoMapping) {
|
||||
return true;
|
||||
}
|
||||
if (AutoMappingStrategy::NONE !== $strategy = $metadata->getAutoMappingStrategy()) {
|
||||
return AutoMappingStrategy::ENABLED === $strategy;
|
||||
}
|
||||
|
||||
// Fallback on the config
|
||||
|
@ -16,11 +16,10 @@ use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\Type as PropertyInfoType;
|
||||
use Symfony\Component\Validator\Constraints\All;
|
||||
use Symfony\Component\Validator\Constraints\DisableAutoMapping;
|
||||
use Symfony\Component\Validator\Constraints\EnableAutoMapping;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
use Symfony\Component\Validator\Constraints\NotNull;
|
||||
use Symfony\Component\Validator\Constraints\Type;
|
||||
use Symfony\Component\Validator\Mapping\AutoMappingStrategy;
|
||||
use Symfony\Component\Validator\Mapping\ClassMetadata;
|
||||
|
||||
/**
|
||||
@ -77,16 +76,16 @@ final class PropertyInfoLoader implements LoaderInterface
|
||||
$hasNotBlankConstraint = false;
|
||||
$allConstraint = null;
|
||||
foreach ($metadata->getPropertyMetadata($property) as $propertyMetadata) {
|
||||
// Enabling or disabling auto-mapping explicitly always takes precedence
|
||||
if (AutoMappingStrategy::DISABLED === $propertyMetadata->getAutoMappingStrategy()) {
|
||||
continue 2;
|
||||
}
|
||||
|
||||
if (AutoMappingStrategy::ENABLED === $propertyMetadata->getAutoMappingStrategy()) {
|
||||
$enabledForProperty = true;
|
||||
}
|
||||
|
||||
foreach ($propertyMetadata->getConstraints() as $constraint) {
|
||||
// Enabling or disabling auto-mapping explicitly always takes precedence
|
||||
if ($constraint instanceof DisableAutoMapping) {
|
||||
continue 3;
|
||||
}
|
||||
|
||||
if ($constraint instanceof EnableAutoMapping) {
|
||||
$enabledForProperty = true;
|
||||
}
|
||||
|
||||
if ($constraint instanceof Type) {
|
||||
$hasTypeConstraint = true;
|
||||
} elseif ($constraint instanceof NotNull) {
|
||||
|
@ -53,10 +53,7 @@ abstract class AbstractComparisonValidatorTestCase extends ConstraintValidatorTe
|
||||
|
||||
foreach ($comparison as $i => $value) {
|
||||
if ($value instanceof \DateTime) {
|
||||
$comparison[$i] = new \DateTimeImmutable(
|
||||
$value->format('Y-m-d H:i:s.u e'),
|
||||
$value->getTimezone()
|
||||
);
|
||||
$comparison[$i] = new \DateTimeImmutable($value->format('Y-m-d H:i:s.u e'));
|
||||
$add = true;
|
||||
} elseif ('DateTime' === $value) {
|
||||
$comparison[$i] = 'DateTimeImmutable';
|
||||
|
@ -15,13 +15,14 @@ use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
use Symfony\Component\Validator\Constraints\All;
|
||||
use Symfony\Component\Validator\Constraints\DisableAutoMapping;
|
||||
use Symfony\Component\Validator\Constraints\Iban;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
use Symfony\Component\Validator\Constraints\NotNull;
|
||||
use Symfony\Component\Validator\Constraints\Type as TypeConstraint;
|
||||
use Symfony\Component\Validator\Mapping\AutoMappingStrategy;
|
||||
use Symfony\Component\Validator\Mapping\ClassMetadata;
|
||||
use Symfony\Component\Validator\Mapping\Loader\PropertyInfoLoader;
|
||||
use Symfony\Component\Validator\Mapping\PropertyMetadata;
|
||||
use Symfony\Component\Validator\Tests\Fixtures\Entity;
|
||||
use Symfony\Component\Validator\Tests\Fixtures\PropertyInfoLoaderEntity;
|
||||
use Symfony\Component\Validator\Tests\Fixtures\PropertyInfoLoaderNoAutoMappingEntity;
|
||||
@ -164,11 +165,12 @@ class PropertyInfoLoaderTest extends TestCase
|
||||
$readOnlyMetadata = $classMetadata->getPropertyMetadata('readOnly');
|
||||
$this->assertEmpty($readOnlyMetadata);
|
||||
|
||||
/** @var PropertyMetadata[] $noAutoMappingMetadata */
|
||||
$noAutoMappingMetadata = $classMetadata->getPropertyMetadata('noAutoMapping');
|
||||
$this->assertCount(1, $noAutoMappingMetadata);
|
||||
$this->assertSame(AutoMappingStrategy::DISABLED, $noAutoMappingMetadata[0]->getAutoMappingStrategy());
|
||||
$noAutoMappingConstraints = $noAutoMappingMetadata[0]->getConstraints();
|
||||
$this->assertCount(1, $noAutoMappingConstraints);
|
||||
$this->assertInstanceOf(DisableAutoMapping::class, $noAutoMappingConstraints[0]);
|
||||
$this->assertCount(0, $noAutoMappingConstraints, 'DisableAutoMapping constraint is not added in the list');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -222,8 +224,10 @@ class PropertyInfoLoaderTest extends TestCase
|
||||
->getValidator()
|
||||
;
|
||||
|
||||
/** @var ClassMetadata $classMetadata */
|
||||
$classMetadata = $validator->getMetadataFor(new PropertyInfoLoaderNoAutoMappingEntity());
|
||||
$this->assertEmpty($classMetadata->getPropertyMetadata('string'));
|
||||
$this->assertCount(3, $classMetadata->getPropertyMetadata('autoMappingExplicitlyEnabled')[0]->constraints);
|
||||
$this->assertCount(2, $classMetadata->getPropertyMetadata('autoMappingExplicitlyEnabled')[0]->constraints);
|
||||
$this->assertSame(AutoMappingStrategy::ENABLED, $classMetadata->getPropertyMetadata('autoMappingExplicitlyEnabled')[0]->getAutoMappingStrategy());
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ class Dumper
|
||||
$blockIndentationIndicator = (' ' === substr($value, 0, 1)) ? (string) $this->indentation : '';
|
||||
$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);
|
||||
}
|
||||
|
||||
@ -80,6 +80,19 @@ class Dumper
|
||||
if ($value instanceof TaggedValue) {
|
||||
$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())) {
|
||||
$output .= ' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n";
|
||||
} else {
|
||||
|
@ -487,6 +487,39 @@ YAML;
|
||||
$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()
|
||||
{
|
||||
$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
Block a user