Merge branch '4.4' into 5.0
* 4.4: [travis] fix CI (ter) Revert "[travis][appveyor] don't cache .phpunit" silence E_NOTICE triggered since PHP 7.4 [Form] Removed legacy check in `ValidationListener` [HttpClient] fix HTTP/2 support on non-SSL connections - CurlHttpClient only Force ping after transport Exception do not merge constraints within interfaces [Validator] Fixed default group for nested composite constraints
This commit is contained in:
commit
7e739eeee2
|
@ -4,6 +4,7 @@ clone_folder: c:\projects\symfony
|
|||
|
||||
cache:
|
||||
- composer.phar
|
||||
- .phpunit -> phpunit
|
||||
|
||||
init:
|
||||
- SET PATH=c:\php;%PATH%
|
||||
|
|
11
.travis.yml
11
.travis.yml
|
@ -35,6 +35,7 @@ matrix:
|
|||
|
||||
cache:
|
||||
directories:
|
||||
- .phpunit
|
||||
- php-$MIN_PHP
|
||||
- ~/php-ext
|
||||
|
||||
|
@ -270,8 +271,10 @@ install:
|
|||
if [[ !$deps && $PHP = 7.2 ]]; then
|
||||
phpenv global $PHP
|
||||
tfold 'composer update' $COMPOSER_UP
|
||||
tfold 'phpunit install' ./phpunit install
|
||||
tfold src/Symfony/Component/HttpClient.h2push "docker run -it --rm -v $(pwd):/app -v /usr/local/bin/vulcain:/usr/local/bin/vulcain -w /app php:7.3-alpine ./phpunit src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php --filter testHttp2Push"
|
||||
[ -d .phpunit ] && mv .phpunit .phpunit.bak
|
||||
tfold src/Symfony/Component/HttpClient.h2push "docker run -it --rm -v $(pwd):/app -v $(phpenv which composer):/usr/local/bin/composer -v /usr/local/bin/vulcain:/usr/local/bin/vulcain -w /app php:7.3-alpine ./phpunit src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php --filter testHttp2Push"
|
||||
sudo rm .phpunit -rf
|
||||
[ -d .phpunit.bak ] && mv .phpunit.bak .phpunit
|
||||
fi
|
||||
|
||||
if [[ $PHP != 7.4* && $PHP != $TRAVIS_PHP_VERSION && $TRAVIS_PULL_REQUEST != false ]]; then
|
||||
|
@ -280,12 +283,10 @@ install:
|
|||
fi
|
||||
|
||||
([[ $deps ]] && cd src/Symfony/Component/HttpFoundation; cp composer.json composer.bak; composer config platform.ext-mongodb 1.6.0; composer require --dev --no-update mongodb/mongodb ~1.5.0)
|
||||
|
||||
if [[ $deps || $PHP != 7.2 ]]; then
|
||||
tfold 'composer update' $COMPOSER_UP
|
||||
tfold 'phpunit install' ./phpunit install
|
||||
fi
|
||||
|
||||
tfold 'phpunit install' ./phpunit install
|
||||
if [[ $deps = high ]]; then
|
||||
echo "$COMPONENTS" | parallel --gnu "tfold {} 'cd {} && $COMPOSER_UP && $PHPUNIT_X$LEGACY'" || X=1
|
||||
(cd src/Symfony/Component/HttpFoundation; mv composer.bak composer.json)
|
||||
|
|
2
phpunit
2
phpunit
|
@ -1,6 +1,8 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
// Cache-Id: 2020-04-10 20:30 UTC
|
||||
|
||||
if (!file_exists(__DIR__.'/vendor/symfony/phpunit-bridge/bin/simple-phpunit')) {
|
||||
echo "Unable to find the `simple-phpunit` script in `vendor/symfony/phpunit-bridge/bin/`.\nPlease run `composer update` before running this command.\n";
|
||||
exit(1);
|
||||
|
|
|
@ -50,8 +50,7 @@ class ValidationListener implements EventSubscriberInterface
|
|||
foreach ($this->validator->validate($form) as $violation) {
|
||||
// Allow the "invalid" constraint to be put onto
|
||||
// non-synchronized forms
|
||||
// ConstraintViolation::getConstraint() must not expect to provide a constraint as long as Symfony\Component\Validator\ExecutionContext exists (before 3.0)
|
||||
$allowNonSynchronized = (null === $violation->getConstraint() || $violation->getConstraint() instanceof Form) && Form::NOT_SYNCHRONIZED_ERROR === $violation->getCode();
|
||||
$allowNonSynchronized = $violation->getConstraint() instanceof Form && Form::NOT_SYNCHRONIZED_ERROR === $violation->getCode();
|
||||
|
||||
$this->violationMapper->mapViolation($violation, $form, $allowNonSynchronized);
|
||||
}
|
||||
|
|
|
@ -141,12 +141,12 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface,
|
|||
CURLOPT_CERTINFO => $options['capture_peer_cert_chain'],
|
||||
];
|
||||
|
||||
if (1.0 === (float) $options['http_version']) {
|
||||
$curlopts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0;
|
||||
} elseif (1.1 === (float) $options['http_version'] || 'https:' !== $scheme) {
|
||||
$curlopts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1;
|
||||
} elseif (\defined('CURL_VERSION_HTTP2') && CURL_VERSION_HTTP2 & self::$curlVersion['features']) {
|
||||
if (\defined('CURL_VERSION_HTTP2') && (CURL_VERSION_HTTP2 & self::$curlVersion['features']) && ('https:' === $scheme || 2.0 === (float) $options['http_version'])) {
|
||||
$curlopts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0;
|
||||
} elseif (1.0 === (float) $options['http_version']) {
|
||||
$curlopts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0;
|
||||
} elseif (1.1 === (float) $options['http_version']) {
|
||||
$curlopts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1;
|
||||
}
|
||||
|
||||
if (isset($options['auth_ntlm'])) {
|
||||
|
|
|
@ -79,7 +79,7 @@ class Logger extends AbstractLogger
|
|||
}
|
||||
|
||||
$formatter = $this->formatter;
|
||||
fwrite($this->handle, $formatter($level, $message, $context));
|
||||
@fwrite($this->handle, $formatter($level, $message, $context));
|
||||
}
|
||||
|
||||
private function format(string $level, string $message, array $context): string
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Mailer\Tests\Transport\Smtp;
|
|||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Mailer\Envelope;
|
||||
use Symfony\Component\Mailer\Exception\TransportException;
|
||||
use Symfony\Component\Mailer\Transport\Smtp\SmtpTransport;
|
||||
use Symfony\Component\Mailer\Transport\Smtp\Stream\AbstractStream;
|
||||
use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream;
|
||||
|
@ -43,6 +44,29 @@ class SmtpTransportTest extends TestCase
|
|||
$this->assertNotContains("NOOP\r\n", $stream->getCommands());
|
||||
}
|
||||
|
||||
public function testSendPingAfterTransportException(): void
|
||||
{
|
||||
$stream = new DummyStream();
|
||||
$envelope = new Envelope(new Address('sender@example.org'), [new Address('recipient@example.org')]);
|
||||
|
||||
$transport = new SmtpTransport($stream);
|
||||
$transport->send(new RawMessage('Message 1'), $envelope);
|
||||
$stream->close();
|
||||
$catch = false;
|
||||
|
||||
try {
|
||||
$transport->send(new RawMessage('Message 2'), $envelope);
|
||||
} catch (TransportException $exception) {
|
||||
$catch = true;
|
||||
}
|
||||
$this->assertTrue($catch);
|
||||
$this->assertTrue($stream->isClosed());
|
||||
|
||||
$transport->send(new RawMessage('Message 3'), $envelope);
|
||||
|
||||
$this->assertFalse($stream->isClosed());
|
||||
}
|
||||
|
||||
public function testSendDoesPingAboveThreshold(): void
|
||||
{
|
||||
$stream = new DummyStream();
|
||||
|
@ -76,13 +100,23 @@ class DummyStream extends AbstractStream
|
|||
*/
|
||||
private $commands;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $closed = true;
|
||||
|
||||
public function initialize(): void
|
||||
{
|
||||
$this->closed = false;
|
||||
$this->nextResponse = '220 localhost';
|
||||
}
|
||||
|
||||
public function write(string $bytes, $debug = true): void
|
||||
{
|
||||
if ($this->closed) {
|
||||
throw new TransportException('Unable to write bytes on the wire.');
|
||||
}
|
||||
|
||||
$this->commands[] = $bytes;
|
||||
|
||||
if (0 === strpos($bytes, 'DATA')) {
|
||||
|
@ -120,4 +154,14 @@ class DummyStream extends AbstractStream
|
|||
{
|
||||
return 'null';
|
||||
}
|
||||
|
||||
public function close(): void
|
||||
{
|
||||
$this->closed = true;
|
||||
}
|
||||
|
||||
public function isClosed(): bool
|
||||
{
|
||||
return $this->closed;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -206,12 +206,11 @@ class SmtpTransport extends AbstractTransport
|
|||
$this->stream->flush();
|
||||
$this->executeCommand("\r\n.\r\n", [250]);
|
||||
$message->appendDebug($this->stream->getDebug());
|
||||
$this->lastMessageTime = microtime(true);
|
||||
} catch (TransportExceptionInterface $e) {
|
||||
$e->appendDebug($this->stream->getDebug());
|
||||
|
||||
$this->lastMessageTime = 0;
|
||||
throw $e;
|
||||
} finally {
|
||||
$this->lastMessageTime = microtime(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,8 @@ abstract class Composite extends Constraint
|
|||
}
|
||||
}
|
||||
|
||||
$this->groups = array_keys($mergedGroups);
|
||||
// prevent empty composite constraint to have empty groups
|
||||
$this->groups = array_keys($mergedGroups) ?: [self::DEFAULT_GROUP];
|
||||
$this->$compositeOption = $nestedConstraints;
|
||||
|
||||
return;
|
||||
|
|
|
@ -113,34 +113,25 @@ class LazyLoadingMetadataFactory implements MetadataFactoryInterface
|
|||
|
||||
private function mergeConstraints(ClassMetadata $metadata)
|
||||
{
|
||||
if ($metadata->getReflectionClass()->isInterface()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Include constraints from the parent class
|
||||
if ($parent = $metadata->getReflectionClass()->getParentClass()) {
|
||||
$metadata->mergeConstraints($this->getMetadataFor($parent->name));
|
||||
}
|
||||
|
||||
$interfaces = $metadata->getReflectionClass()->getInterfaces();
|
||||
|
||||
$interfaces = array_filter($interfaces, function (\ReflectionClass $interface) use ($parent, $interfaces) {
|
||||
$interfaceName = $interface->getName();
|
||||
|
||||
if ($parent && $parent->implementsInterface($interfaceName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($interfaces as $i) {
|
||||
if ($i !== $interface && $i->implementsInterface($interfaceName)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
// Include constraints from all directly implemented interfaces
|
||||
foreach ($interfaces as $interface) {
|
||||
foreach ($metadata->getReflectionClass()->getInterfaces() as $interface) {
|
||||
if ('Symfony\Component\Validator\GroupSequenceProviderInterface' === $interface->name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($parent && \in_array($interface->getName(), $parent->getInterfaceNames(), true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$metadata->mergeConstraints($this->getMetadataFor($interface->name));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,4 +100,16 @@ class CollectionTest extends TestCase
|
|||
|
||||
$this->assertEquals($collection1, $collection2);
|
||||
}
|
||||
|
||||
public function testConstraintHasDefaultGroupWithOptionalValues()
|
||||
{
|
||||
$constraint = new Collection([
|
||||
'foo' => new Required(),
|
||||
'bar' => new Optional(),
|
||||
]);
|
||||
|
||||
$this->assertEquals(['Default'], $constraint->groups);
|
||||
$this->assertEquals(['Default'], $constraint->fields['foo']->groups);
|
||||
$this->assertEquals(['Default'], $constraint->fields['bar']->groups);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,6 +143,29 @@ abstract class CollectionValidatorTest extends ConstraintValidatorTestCase
|
|||
->assertRaised();
|
||||
}
|
||||
|
||||
public function testExtraFieldsDisallowedWithOptionalValues()
|
||||
{
|
||||
$constraint = new Optional();
|
||||
|
||||
$data = $this->prepareTestData([
|
||||
'baz' => 6,
|
||||
]);
|
||||
|
||||
$this->validator->validate($data, new Collection([
|
||||
'fields' => [
|
||||
'foo' => $constraint,
|
||||
],
|
||||
'extraFieldsMessage' => 'myMessage',
|
||||
]));
|
||||
|
||||
$this->buildViolation('myMessage')
|
||||
->setParameter('{{ field }}', '"baz"')
|
||||
->atPath('property.path[baz]')
|
||||
->setInvalidValue(6)
|
||||
->setCode(Collection::NO_SUCH_FIELD_ERROR)
|
||||
->assertRaised();
|
||||
}
|
||||
|
||||
// bug fix
|
||||
public function testNullNotConsideredExtraField()
|
||||
{
|
||||
|
|
|
@ -19,7 +19,7 @@ use Symfony\Component\Validator\Constraints\Valid;
|
|||
|
||||
class ConcreteComposite extends Composite
|
||||
{
|
||||
public $constraints;
|
||||
public $constraints = [];
|
||||
|
||||
protected function getCompositeOption(): string
|
||||
{
|
||||
|
@ -37,6 +37,30 @@ class ConcreteComposite extends Composite
|
|||
*/
|
||||
class CompositeTest extends TestCase
|
||||
{
|
||||
public function testConstraintHasDefaultGroup()
|
||||
{
|
||||
$constraint = new ConcreteComposite([
|
||||
new NotNull(),
|
||||
new NotBlank(),
|
||||
]);
|
||||
|
||||
$this->assertEquals(['Default'], $constraint->groups);
|
||||
$this->assertEquals(['Default'], $constraint->constraints[0]->groups);
|
||||
$this->assertEquals(['Default'], $constraint->constraints[1]->groups);
|
||||
}
|
||||
|
||||
public function testNestedCompositeConstraintHasDefaultGroup()
|
||||
{
|
||||
$constraint = new ConcreteComposite([
|
||||
new ConcreteComposite(),
|
||||
new ConcreteComposite(),
|
||||
]);
|
||||
|
||||
$this->assertEquals(['Default'], $constraint->groups);
|
||||
$this->assertEquals(['Default'], $constraint->constraints[0]->groups);
|
||||
$this->assertEquals(['Default'], $constraint->constraints[1]->groups);
|
||||
}
|
||||
|
||||
public function testMergeNestedGroupsIfNoExplicitParentGroup()
|
||||
{
|
||||
$constraint = new ConcreteComposite([
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace Symfony\Component\Validator\Tests\Fixtures;
|
||||
|
||||
abstract class AbstractPropertyGetter implements PropertyGetterInterface
|
||||
{
|
||||
private $property;
|
||||
|
||||
public function getProperty()
|
||||
{
|
||||
return $this->property;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Symfony\Component\Validator\Tests\Fixtures;
|
||||
|
||||
interface ChildGetterInterface extends PropertyGetterInterface
|
||||
{
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace Symfony\Component\Validator\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* This class has two paths to PropertyGetterInterface:
|
||||
* PropertyGetterInterface <- AbstractPropertyGetter <- PropertyGetter
|
||||
* PropertyGetterInterface <- ChildGetterInterface <- PropertyGetter
|
||||
*/
|
||||
class PropertyGetter extends AbstractPropertyGetter implements ChildGetterInterface
|
||||
{
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Symfony\Component\Validator\Tests\Fixtures;
|
||||
|
||||
interface PropertyGetterInterface
|
||||
{
|
||||
public function getProperty();
|
||||
}
|
|
@ -15,10 +15,13 @@ use PHPUnit\Framework\TestCase;
|
|||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
use Symfony\Component\Validator\Constraints\Callback;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
use Symfony\Component\Validator\Mapping\ClassMetadata;
|
||||
use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory;
|
||||
use Symfony\Component\Validator\Mapping\Loader\LoaderInterface;
|
||||
use Symfony\Component\Validator\Tests\Fixtures\ConstraintA;
|
||||
use Symfony\Component\Validator\Tests\Fixtures\PropertyGetter;
|
||||
use Symfony\Component\Validator\Tests\Fixtures\PropertyGetterInterface;
|
||||
|
||||
class LazyLoadingMetadataFactoryTest extends TestCase
|
||||
{
|
||||
|
@ -70,7 +73,6 @@ class LazyLoadingMetadataFactoryTest extends TestCase
|
|||
new ConstraintA(['groups' => [
|
||||
'Default',
|
||||
'EntityParentInterface',
|
||||
'EntityInterfaceB',
|
||||
'Entity',
|
||||
]]),
|
||||
];
|
||||
|
@ -150,6 +152,15 @@ class LazyLoadingMetadataFactoryTest extends TestCase
|
|||
$this->assertContains('EntityStaticCar', $groups);
|
||||
$this->assertContains('EntityStaticVehicle', $groups);
|
||||
}
|
||||
|
||||
public function testMultipathInterfaceConstraint()
|
||||
{
|
||||
$factory = new LazyLoadingMetadataFactory(new PropertyGetterInterfaceConstraintLoader());
|
||||
$metadata = $factory->getMetadataFor(PropertyGetter::class);
|
||||
$constraints = $metadata->getPropertyMetadata('property');
|
||||
|
||||
$this->assertCount(1, $constraints);
|
||||
}
|
||||
}
|
||||
|
||||
class TestLoader implements LoaderInterface
|
||||
|
@ -161,3 +172,15 @@ class TestLoader implements LoaderInterface
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class PropertyGetterInterfaceConstraintLoader implements LoaderInterface
|
||||
{
|
||||
public function loadClassMetadata(ClassMetadata $metadata)
|
||||
{
|
||||
if (PropertyGetterInterface::class === $metadata->getClassName()) {
|
||||
$metadata->addGetterConstraint('property', new NotBlank());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
Reference in New Issue