Merge branch '4.1'

* 4.1:
  Add color support for Hyper terminal .
  [HttpFoundation] Fix tests: new message for status 425
  [Doctrine Bridge] Fixed usage of wrong variable when tagged subscriber is invalid
  [Workflow] Update phpdoc to fit a used className
  [PropertyInfo] added handling of nullable types in PhpDoc
  [HttpKernel] Make AbstractTestSessionListener compatible with CookieClearingLogoutHandler
  [Cache] provider does not respect option maxIdLength with versioning enabled
  [Form] Fix fixtures for forward compat
  [Lock] Fix SemaphoreStoreTest on OS X
  Ensure the class discriminator mechanism works with serialization groups as well
  fix handling of empty DI extension configs
This commit is contained in:
Nicolas Grekas 2018-07-03 20:14:13 +02:00
commit a32393ba37
29 changed files with 253 additions and 147 deletions

View File

@ -102,7 +102,7 @@
},
"conflict": {
"phpdocumentor/reflection-docblock": "<3.0||>=3.2.0,<3.2.2",
"phpdocumentor/type-resolver": "<0.2.1",
"phpdocumentor/type-resolver": "<0.3.0",
"phpunit/phpunit": "<5.4.3"
},
"provide": {

View File

@ -69,7 +69,7 @@ class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface
$connections = isset($tag['connection']) ? array($tag['connection']) : array_keys($this->connections);
foreach ($connections as $con) {
if (!isset($this->connections[$con])) {
throw new RuntimeException(sprintf('The Doctrine connection "%s" referenced in service "%s" does not exist. Available connections names: %s', $con, $taggedSubscriber, implode(', ', array_keys($this->connections))));
throw new RuntimeException(sprintf('The Doctrine connection "%s" referenced in service "%s" does not exist. Available connections names: %s', $con, $id, implode(', ', array_keys($this->connections))));
}
$this->getEventManagerDef($container, $con)->addMethodCall('addEventSubscriber', array(new Reference($id)));

View File

@ -316,7 +316,8 @@ class DeprecationErrorHandler
&& sapi_windows_vt100_support(STDOUT))
|| false !== getenv('ANSICON')
|| 'ON' === getenv('ConEmuANSI')
|| 'xterm' === getenv('TERM');
|| 'xterm' === getenv('TERM')
|| 'Hyper' === getenv('TERM_PROGRAM');
}
if (function_exists('stream_isatty')) {

View File

@ -34,6 +34,34 @@ class MaxIdLengthAdapterTest extends TestCase
$cache->hasItem(str_repeat('-', 39));
}
public function testLongKeyVersioning()
{
$cache = $this->getMockBuilder(MaxIdLengthAdapter::class)
->setConstructorArgs(array(str_repeat('-', 26)))
->getMock();
$reflectionClass = new \ReflectionClass(AbstractAdapter::class);
$reflectionMethod = $reflectionClass->getMethod('getId');
$reflectionMethod->setAccessible(true);
// No versioning enabled
$this->assertEquals('--------------------------:------------', $reflectionMethod->invokeArgs($cache, array(str_repeat('-', 12))));
$this->assertLessThanOrEqual(50, strlen($reflectionMethod->invokeArgs($cache, array(str_repeat('-', 12)))));
$this->assertLessThanOrEqual(50, strlen($reflectionMethod->invokeArgs($cache, array(str_repeat('-', 23)))));
$this->assertLessThanOrEqual(50, strlen($reflectionMethod->invokeArgs($cache, array(str_repeat('-', 40)))));
$reflectionProperty = $reflectionClass->getProperty('versioningIsEnabled');
$reflectionProperty->setAccessible(true);
$reflectionProperty->setValue($cache, true);
// Versioning enabled
$this->assertEquals('--------------------------:1:------------', $reflectionMethod->invokeArgs($cache, array(str_repeat('-', 12))));
$this->assertLessThanOrEqual(50, strlen($reflectionMethod->invokeArgs($cache, array(str_repeat('-', 12)))));
$this->assertLessThanOrEqual(50, strlen($reflectionMethod->invokeArgs($cache, array(str_repeat('-', 23)))));
$this->assertLessThanOrEqual(50, strlen($reflectionMethod->invokeArgs($cache, array(str_repeat('-', 40)))));
}
/**
* @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException
* @expectedExceptionMessage Namespace must be 26 chars max, 40 given ("----------------------------------------")

View File

@ -122,6 +122,7 @@ trait AbstractTrait
}
if ($cleared = true === $cleared || array() === $cleared) {
$this->namespaceVersion = $namespaceVersion;
$this->ids = array();
}
}
@ -196,6 +197,7 @@ trait AbstractTrait
$wasEnabled = $this->versioningIsEnabled;
$this->versioningIsEnabled = (bool) $enable;
$this->namespaceVersion = '';
$this->ids = array();
return $wasEnabled;
}
@ -242,6 +244,7 @@ trait AbstractTrait
private function getId($key)
{
if ($this->versioningIsEnabled && '' === $this->namespaceVersion) {
$this->ids = array();
$this->namespaceVersion = '1:';
try {
foreach ($this->doFetch(array('@'.$this->namespace)) as $v) {
@ -262,7 +265,7 @@ trait AbstractTrait
}
if (\strlen($id = $this->namespace.$this->namespaceVersion.$key) > $this->maxIdLength) {
// Use MD5 to favor speed over security, which is not an issue here
$this->ids[$key] = $id = substr_replace(base64_encode(hash('md5', $key, true)), ':', -2);
$this->ids[$key] = $id = substr_replace(base64_encode(hash('md5', $key, true)), ':', -(\strlen($this->namespaceVersion) + 2));
$id = $this->namespace.$this->namespaceVersion.$id;
}

View File

@ -98,7 +98,8 @@ class StreamOutput extends Output
&& @sapi_windows_vt100_support($this->stream))
|| false !== getenv('ANSICON')
|| 'ON' === getenv('ConEmuANSI')
|| 'xterm' === getenv('TERM');
|| 'xterm' === getenv('TERM')
|| 'Hyper' === getenv('TERM_PROGRAM');
}
if (function_exists('stream_isatty')) {

View File

@ -269,7 +269,7 @@ class SymfonyStyle extends OutputStyle
{
$progressBar = parent::createProgressBar($max);
if ('\\' !== DIRECTORY_SEPARATOR) {
if ('\\' !== DIRECTORY_SEPARATOR || 'Hyper' === getenv('TERM_PROGRAM')) {
$progressBar->setEmptyBarCharacter('░'); // light shade character \u2591
$progressBar->setProgressCharacter('');
$progressBar->setBarCharacter('▓'); // dark shade character \u2593

View File

@ -65,7 +65,7 @@ class ValidateEnvPlaceholdersPass implements CompilerPassInterface
$processor = new Processor();
foreach ($extensions as $name => $extension) {
if (!$extension instanceof ConfigurationExtensionInterface || !$config = $container->getExtensionConfig($name)) {
if (!$extension instanceof ConfigurationExtensionInterface || !$config = array_filter($container->getExtensionConfig($name))) {
// this extension has no semantic configuration or was not called
continue;
}

View File

@ -15,9 +15,9 @@ Symfony\Component\Form\Extension\Core\Type\ChoiceType (choice_translation_domain
---------------- --------------------%s
Allowed values - %s
---------------- --------------------%s
Normalizer Closure%s{ %s
Normalizer Closure%s{%w
parameters: 2 %s
file: "%s%eExtension%eCore%eType%eChoiceType.php"
file: "%s%eExtension%eCore%eType%eChoiceType.php"%w
line: "%s to %s" %s
} %s
---------------- --------------------%s

View File

@ -8,14 +8,14 @@ Symfony\Component\Form\Tests\Console\Descriptor\FooType (empty_data)
Default Value: null %s
%s
Closure(s): [ %s
Closure%s{ %s
Closure%s{%w
parameters: 1 %s
file: "%s%eExtension%eCore%eType%eFormType.php"
file: "%s%eExtension%eCore%eType%eFormType.php"%w
line: "%s to %s" %s
}, %s
Closure%s{ %s
Closure%s{%w
parameters: 2 %s
file: "%s%eTests%eConsole%eDescriptor%eAbstractDescriptorTest.php"
file: "%s%eTests%eConsole%eDescriptor%eAbstractDescriptorTest.php"%w
line: "%s to %s" %s
} %s
] %s

View File

@ -16,9 +16,9 @@ Symfony\Component\Form\Tests\Console\Descriptor\FooType (foo)
"baz" %s
] %s
---------------- --------------------%s
Normalizer Closure%s{ %s
Normalizer Closure%s{%w
parameters: 2 %s
file: "%s%eTests%eConsole%eDescriptor%eAbstractDescriptorTest.php"
file: "%s%eTests%eConsole%eDescriptor%eAbstractDescriptorTest.php"%w
line: "%s to %s" %s
} %s
---------------- --------------------%s

View File

@ -64,7 +64,12 @@ class Response
const HTTP_UNPROCESSABLE_ENTITY = 422; // RFC4918
const HTTP_LOCKED = 423; // RFC4918
const HTTP_FAILED_DEPENDENCY = 424; // RFC4918
/**
* @deprecated
*/
const HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425; // RFC2817
const HTTP_TOO_EARLY = 425; // RFC-ietf-httpbis-replay-04
const HTTP_UPGRADE_REQUIRED = 426; // RFC2817
const HTTP_PRECONDITION_REQUIRED = 428; // RFC6585
const HTTP_TOO_MANY_REQUESTS = 429; // RFC6585
@ -169,7 +174,7 @@ class Response
422 => 'Unprocessable Entity', // RFC4918
423 => 'Locked', // RFC4918
424 => 'Failed Dependency', // RFC4918
425 => 'Reserved for WebDAV advanced collections expired proposal', // RFC2817
425 => 'Too Early', // RFC-ietf-httpbis-replay-04
426 => 'Upgrade Required', // RFC2817
428 => 'Precondition Required', // RFC6585
429 => 'Too Many Requests', // RFC6585

View File

@ -73,6 +73,13 @@ abstract class AbstractTestSessionListener implements EventSubscriberInterface
if ($session instanceof Session ? !$session->isEmpty() || (null !== $this->sessionId && $session->getId() !== $this->sessionId) : $wasStarted) {
$params = session_get_cookie_params();
foreach ($event->getResponse()->headers->getCookies() as $cookie) {
if ($session->getName() === $cookie->getName() && $params['path'] === $cookie->getPath() && $params['domain'] == $cookie->getDomain()) {
return;
}
}
$event->getResponse()->headers->setCookie(new Cookie($session->getName(), $session->getId(), 0 === $params['lifetime'] ? 0 : time() + $params['lifetime'], $params['path'], $params['domain'], $params['secure'], $params['httponly']));
$this->sessionId = $session->getId();
}

View File

@ -106,6 +106,36 @@ class TestSessionListenerTest extends TestCase
$this->assertNotEmpty($response->headers->getCookies());
}
/**
* @dataProvider anotherCookieProvider
*/
public function testSessionWithNewSessionIdAndNewCookieDoesNotSendAnotherCookie($existing, array $expected)
{
$this->sessionHasBeenStarted();
$this->sessionIsEmpty();
$this->fixSessionId('456');
$kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
$request = Request::create('/', 'GET', array(), array('MOCKSESSID' => '123'));
$event = new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST);
$this->listener->onKernelRequest($event);
$response = new Response('', 200, array('Set-Cookie' => $existing));
$response = $this->filterResponse(new Request(), HttpKernelInterface::MASTER_REQUEST, $response);
$this->assertSame($expected, $response->headers->get('Set-Cookie', null, false));
}
public function anotherCookieProvider()
{
return array(
'same' => array('MOCKSESSID=789; path=/', array('MOCKSESSID=789; path=/')),
'different domain' => array('MOCKSESSID=789; path=/; domain=example.com', array('MOCKSESSID=789; path=/; domain=example.com', 'MOCKSESSID=456; path=/')),
'different path' => array('MOCKSESSID=789; path=/foo', array('MOCKSESSID=789; path=/foo', 'MOCKSESSID=456; path=/')),
);
}
public function testUnstartedSessionIsNotSave()
{
$this->sessionHasNotBeenStarted();
@ -133,10 +163,10 @@ class TestSessionListenerTest extends TestCase
$this->assertTrue(true);
}
private function filterResponse(Request $request, $type = HttpKernelInterface::MASTER_REQUEST)
private function filterResponse(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, Response $response = null)
{
$request->setSession($this->session);
$response = new Response();
$response = $response ?: new Response();
$kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
$event = new FilterResponseEvent($kernel, $request, $type, $response);

View File

@ -46,13 +46,22 @@ class SemaphoreStoreTest extends AbstractStoreTest
private function getOpenedSemaphores()
{
if ('Darwin' === PHP_OS) {
$lines = explode(PHP_EOL, trim(`ipcs -s`));
if (-1 === $start = array_search('Semaphores:', $lines)) {
throw new \Exception('Failed to extract list of opened semaphores. Expected a Semaphore list, got '.implode(PHP_EOL, $lines));
}
return \count(\array_slice($lines, ++$start));
}
$lines = explode(PHP_EOL, trim(`LC_ALL=C ipcs -su`));
if ('------ Semaphore Status --------' !== $lines[0]) {
throw new \Exception('Failed to extract list of opend semaphores. Expect a Semaphore status, got '.implode(PHP_EOL, $lines));
throw new \Exception('Failed to extract list of opened semaphores. Expected a Semaphore status, got '.implode(PHP_EOL, $lines));
}
list($key, $value) = explode(' = ', $lines[1]);
if ('used arrays' !== $key) {
throw new \Exception('Failed to extract list of opend semaphores. Expect a used arrays key, got '.implode(PHP_EOL, $lines));
throw new \Exception('Failed to extract list of opened semaphores. Expected a "used arrays" key, got '.implode(PHP_EOL, $lines));
}
return (int) $value;

View File

@ -41,6 +41,21 @@ class PhpDocExtractorTest extends TestCase
$this->assertSame($longDescription, $this->extractor->getLongDescription('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
}
public function testParamTagTypeIsOmitted()
{
$this->assertNull($this->extractor->getTypes(OmittedParamTagTypeDocBlock::class, 'omittedType'));
}
/**
* @dataProvider typesWithNoPrefixesProvider
*/
public function testExtractTypesWithNoPrefixes($property, array $type = null)
{
$noPrefixExtractor = new PhpDocExtractor(null, array(), array(), array());
$this->assertEquals($type, $noPrefixExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
}
public function typesProvider()
{
return array(
@ -69,8 +84,10 @@ class PhpDocExtractorTest extends TestCase
array('d', array(new Type(Type::BUILTIN_TYPE_BOOL)), null, null),
array('e', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_RESOURCE))), null, null),
array('f', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime'))), null, null),
array('g', array(new Type(Type::BUILTIN_TYPE_BOOL, true)), null, null),
array('array', array(new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)), 'Nullable array.', null),
array('g', array(new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)), 'Nullable array.', null),
array('h', array(new Type(Type::BUILTIN_TYPE_STRING, true)), null, null),
array('i', array(new Type(Type::BUILTIN_TYPE_STRING, true), new Type(Type::BUILTIN_TYPE_INT, true)), null, null),
array('j', array(new Type(Type::BUILTIN_TYPE_OBJECT, true, 'DateTime')), null, null),
array('donotexist', null, null, null),
array('staticGetter', null, null, null),
array('staticSetter', null, null, null),
@ -111,11 +128,6 @@ class PhpDocExtractorTest extends TestCase
);
}
public function testParamTagTypeIsOmitted()
{
$this->assertNull($this->extractor->getTypes(OmittedParamTagTypeDocBlock::class, 'omittedType'));
}
/**
* @dataProvider typesWithCustomPrefixesProvider
*/
@ -154,23 +166,16 @@ class PhpDocExtractorTest extends TestCase
array('d', array(new Type(Type::BUILTIN_TYPE_BOOL)), null, null),
array('e', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_RESOURCE))), null, null),
array('f', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime'))), null, null),
array('g', null),
array('g', array(new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)), 'Nullable array.', null),
array('h', array(new Type(Type::BUILTIN_TYPE_STRING, true)), null, null),
array('i', array(new Type(Type::BUILTIN_TYPE_STRING, true), new Type(Type::BUILTIN_TYPE_INT, true)), null, null),
array('j', array(new Type(Type::BUILTIN_TYPE_OBJECT, true, 'DateTime')), null, null),
array('donotexist', null, null, null),
array('staticGetter', null, null, null),
array('staticSetter', null, null, null),
);
}
/**
* @dataProvider typesWithNoPrefixesProvider
*/
public function testExtractTypesWithNoPrefixes($property, array $type = null)
{
$noPrefixExtractor = new PhpDocExtractor(null, array(), array(), array());
$this->assertEquals($type, $noPrefixExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
}
public function typesWithNoPrefixesProvider()
{
return array(
@ -199,7 +204,10 @@ class PhpDocExtractorTest extends TestCase
array('d', null, null, null),
array('e', null, null, null),
array('f', null, null, null),
array('g', null),
array('g', array(new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)), 'Nullable array.', null),
array('h', array(new Type(Type::BUILTIN_TYPE_STRING, true)), null, null),
array('i', array(new Type(Type::BUILTIN_TYPE_STRING, true), new Type(Type::BUILTIN_TYPE_INT, true)), null, null),
array('j', array(new Type(Type::BUILTIN_TYPE_OBJECT, true, 'DateTime')), null, null),
array('donotexist', null, null, null),
array('staticGetter', null, null, null),
array('staticSetter', null, null, null),

View File

@ -40,7 +40,10 @@ class ReflectionExtractorTest extends TestCase
'collection',
'B',
'Guid',
'array',
'g',
'h',
'i',
'j',
'emptyVar',
'iteratorCollection',
'iteratorCollectionWithKey',
@ -61,7 +64,6 @@ class ReflectionExtractorTest extends TestCase
'd',
'e',
'f',
'g',
),
$this->extractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy')
);
@ -80,7 +82,10 @@ class ReflectionExtractorTest extends TestCase
'collection',
'B',
'Guid',
'array',
'g',
'h',
'i',
'j',
'emptyVar',
'iteratorCollection',
'iteratorCollectionWithKey',
@ -111,7 +116,10 @@ class ReflectionExtractorTest extends TestCase
'collection',
'B',
'Guid',
'array',
'g',
'h',
'i',
'j',
'emptyVar',
'iteratorCollection',
'iteratorCollectionWithKey',
@ -127,24 +135,6 @@ class ReflectionExtractorTest extends TestCase
);
}
public function testGetPropertiesPhp71()
{
$noPrefixExtractor = new ReflectionExtractor();
$this->assertSame(
array(
'string',
'stringOrNull',
'foo',
'buz',
'bar',
'baz',
'intWithAccessor',
),
$noPrefixExtractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\Php71Dummy')
);
}
/**
* @dataProvider typesProvider
*/
@ -206,22 +196,9 @@ class ReflectionExtractorTest extends TestCase
array('bar', array(new Type(Type::BUILTIN_TYPE_INT, true))),
array('baz', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING)))),
array('donotexist', null),
array('string', array(new Type(Type::BUILTIN_TYPE_STRING, false))),
array('stringOrNull', array(new Type(Type::BUILTIN_TYPE_STRING, true))),
array('intPrivate', array(new Type(Type::BUILTIN_TYPE_INT, false))),
array('intWithAccessor', array(new Type(Type::BUILTIN_TYPE_INT, false))),
);
}
public function testExtractPhp71TypeWithParentConstructor()
{
$property = 'string';
$type = array(new Type(Type::BUILTIN_TYPE_STRING, false));
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php71DummyChild', $property, array()));
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php71DummyChild2', $property, array()));
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php71DummyChild3', $property, array()));
}
/**
* @dataProvider getReadableProperties
*/
@ -245,12 +222,10 @@ class ReflectionExtractorTest extends TestCase
array('d', true),
array('e', false),
array('f', false),
array('g', true),
array('Id', true),
array('id', true),
array('Guid', true),
array('guid', false),
array('guid', false),
);
}
@ -277,7 +252,6 @@ class ReflectionExtractorTest extends TestCase
array('d', false),
array('e', true),
array('f', true),
array('g', false),
array('Id', false),
array('Guid', true),
array('guid', false),

View File

@ -66,7 +66,22 @@ class Dummy extends ParentDummy
*
* @var array|null
*/
public $array;
public $g;
/**
* @var ?string
*/
public $h;
/**
* @var ?string|int
*/
public $i;
/**
* @var ?\DateTime
*/
public $j;
/**
* This should not be removed.

View File

@ -75,11 +75,4 @@ class ParentDummy
public function removeF(\DateTime $f)
{
}
/**
* @return bool|null
*/
public function hasG()
{
}
}

View File

@ -16,22 +16,6 @@ namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
*/
class Php71Dummy
{
public $string;
public $stringOrNull;
private $intPrivate;
private $intWithAccessor;
public function __construct(string $string, ?string $stringOrNull, int $intPrivate, int $intWithAccessor)
{
$this->string = $string;
$this->stringOrNull = $stringOrNull;
$this->intPrivate = $intPrivate;
$this->intWithAccessor = $intWithAccessor;
}
public function getFoo(): ?array
{
}
@ -47,9 +31,4 @@ class Php71Dummy
public function addBaz(string $baz)
{
}
public function getIntWithAccessor()
{
return $this->intWithAccessor;
}
}

View File

@ -15,6 +15,7 @@ use phpDocumentor\Reflection\Type as DocType;
use phpDocumentor\Reflection\Types\Collection;
use phpDocumentor\Reflection\Types\Compound;
use phpDocumentor\Reflection\Types\Null_;
use phpDocumentor\Reflection\Types\Nullable;
use Symfony\Component\PropertyInfo\Type;
/**
@ -35,6 +36,11 @@ final class PhpDocTypeHelper
$types = array();
$nullable = false;
if ($varType instanceof Nullable) {
$nullable = true;
$varType = $varType->getActualType();
}
if (!$varType instanceof Compound) {
if ($varType instanceof Null_) {
$nullable = true;

View File

@ -35,7 +35,7 @@
},
"conflict": {
"phpdocumentor/reflection-docblock": "<3.0||>=3.2.0,<3.2.2",
"phpdocumentor/type-resolver": "<0.2.1",
"phpdocumentor/type-resolver": "<0.3.0",
"symfony/dependency-injection": "<3.4"
},
"suggest": {

View File

@ -16,6 +16,7 @@ use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
use Symfony\Component\Serializer\Exception\RuntimeException;
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
@ -131,4 +132,24 @@ class ObjectNormalizer extends AbstractObjectNormalizer
// Properties not found are ignored
}
}
/**
* {@inheritdoc}
*/
protected function getAllowedAttributes($classOrObject, array $context, $attributesAsString = false)
{
if (false === $allowedAttributes = parent::getAllowedAttributes($classOrObject, $context, $attributesAsString)) {
return false;
}
if (null !== $this->classDiscriminatorResolver && null !== $discriminatorMapping = $this->classDiscriminatorResolver->getMappingForMappedObject($classOrObject)) {
$allowedAttributes[] = $attributesAsString ? $discriminatorMapping->getTypeProperty() : new AttributeMetadata($discriminatorMapping->getTypeProperty());
foreach ($discriminatorMapping->getTypesMapping() as $class) {
$allowedAttributes = array_merge($allowedAttributes, parent::getAllowedAttributes($class, $context, $attributesAsString));
}
}
return $allowedAttributes;
}
}

View File

@ -15,8 +15,8 @@ use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
/**
* @DiscriminatorMap(typeProperty="type", mapping={
* "first"="Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild",
* "second"="Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild"
* "one"="Symfony\Component\Serializer\Tests\Fixtures\DummyMessageNumberOne",
* "two"="Symfony\Component\Serializer\Tests\Fixtures\DummyMessageNumberTwo"
* })
*
* @author Samuel Roze <samuel.roze@gmail.com>

View File

@ -11,10 +11,17 @@
namespace Symfony\Component\Serializer\Tests\Fixtures;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* @author Samuel Roze <samuel.roze@gmail.com>
*/
class DummyMessageNumberOne implements DummyMessageInterface
{
public $one;
/**
* @Groups({"two"})
*/
public $two;
}

View File

@ -0,0 +1,19 @@
<?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\Serializer\Tests\Fixtures;
/**
* @author Samuel Roze <samuel.roze@gmail.com>
*/
class DummyMessageNumberTwo implements DummyMessageInterface
{
}

View File

@ -11,11 +11,14 @@
namespace Symfony\Component\Serializer\Tests;
use Doctrine\Common\Annotations\AnnotationReader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping;
use Symfony\Component\Serializer\Mapping\ClassMetadata;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
@ -398,11 +401,9 @@ class SerializerTest extends TestCase
$example = new DummyMessageNumberOne();
$example->one = 1;
$jsonData = '{"message-type":"one","one":1}';
$discriminatorResolver = new ClassDiscriminatorFromClassMetadata($this->metadataFactoryMockForDummyInterface());
$serializer = new Serializer(array(new ObjectNormalizer(null, null, null, null, $discriminatorResolver)), array('json' => new JsonEncoder()));
$jsonData = '{"type":"one","one":1,"two":null}';
$serializer = $this->serializerWithClassDiscriminator();
$deserialized = $serializer->deserialize($jsonData, DummyMessageInterface::class, 'json');
$this->assertEquals($example, $deserialized);
@ -410,51 +411,48 @@ class SerializerTest extends TestCase
$this->assertEquals($jsonData, $serialized);
}
public function testDeserializeAndSerializeInterfacedObjectsWithTheClassMetadataDiscriminatorResolverAndGroups()
{
$example = new DummyMessageNumberOne();
$example->two = 2;
$serializer = $this->serializerWithClassDiscriminator();
$deserialized = $serializer->deserialize('{"type":"one","one":1,"two":2}', DummyMessageInterface::class, 'json', array(
'groups' => array('two'),
));
$this->assertEquals($example, $deserialized);
$serialized = $serializer->serialize($deserialized, 'json', array(
'groups' => array('two'),
));
$this->assertEquals('{"two":2,"type":"one"}', $serialized);
}
/**
* @expectedException \Symfony\Component\Serializer\Exception\RuntimeException
* @expectedExceptionMessage The type "second" has no mapped class for the abstract object "Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface"
*/
public function testExceptionWhenTypeIsNotKnownInDiscriminator()
{
$discriminatorResolver = new ClassDiscriminatorFromClassMetadata($this->metadataFactoryMockForDummyInterface());
$serializer = new Serializer(array(new ObjectNormalizer(null, null, null, null, $discriminatorResolver)), array('json' => new JsonEncoder()));
$serializer->deserialize('{"message-type":"second","one":1}', DummyMessageInterface::class, 'json');
$this->serializerWithClassDiscriminator()->deserialize('{"type":"second","one":1}', DummyMessageInterface::class, 'json');
}
/**
* @expectedException \Symfony\Component\Serializer\Exception\RuntimeException
* @expectedExceptionMessage Type property "message-type" not found for the abstract object "Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface"
* @expectedExceptionMessage Type property "type" not found for the abstract object "Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface"
*/
public function testExceptionWhenTypeIsNotInTheBodyToDeserialiaze()
{
$discriminatorResolver = new ClassDiscriminatorFromClassMetadata($this->metadataFactoryMockForDummyInterface());
$serializer = new Serializer(array(new ObjectNormalizer(null, null, null, null, $discriminatorResolver)), array('json' => new JsonEncoder()));
$serializer->deserialize('{"one":1}', DummyMessageInterface::class, 'json');
$this->serializerWithClassDiscriminator()->deserialize('{"one":1}', DummyMessageInterface::class, 'json');
}
private function metadataFactoryMockForDummyInterface()
private function serializerWithClassDiscriminator()
{
$factoryMock = $this->getMockBuilder(ClassMetadataFactoryInterface::class)->getMock();
$factoryMock->method('hasMetadataFor')->will($this->returnValueMap(array(
array(
DummyMessageInterface::class,
true,
),
)));
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$factoryMock->method('getMetadataFor')->will($this->returnValueMap(array(
array(
DummyMessageInterface::class,
new ClassMetadata(
DummyMessageInterface::class,
new ClassDiscriminatorMapping('message-type', array(
'one' => DummyMessageNumberOne::class,
))
),
),
)));
return $factoryMock;
return new Serializer(array(new ObjectNormalizer($classMetadataFactory, null, null, null, new ClassDiscriminatorFromClassMetadata($classMetadataFactory))), array('json' => new JsonEncoder()));
}
}

View File

@ -546,7 +546,8 @@ class CliDumper extends AbstractDumper
&& @sapi_windows_vt100_support($stream))
|| false !== getenv('ANSICON')
|| 'ON' === getenv('ConEmuANSI')
|| 'xterm' === getenv('TERM');
|| 'xterm' === getenv('TERM')
|| 'Hyper' === getenv('TERM_PROGRAM');
}
if (function_exists('stream_isatty')) {
@ -575,7 +576,8 @@ class CliDumper extends AbstractDumper
{
$result = 183 <= getenv('ANSICON_VER')
|| 'ON' === getenv('ConEmuANSI')
|| 'xterm' === getenv('TERM');
|| 'xterm' === getenv('TERM')
|| 'Hyper' === getenv('TERM_PROGRAM');
if (!$result && PHP_VERSION_ID >= 70200) {
$version = sprintf(

View File

@ -30,10 +30,10 @@ class Event extends BaseEvent
private $workflowName;
/**
* @param object $subject
* @param Marking $marking
* @param Transition $transition
* @param Workflow $workflow
* @param object $subject
* @param Marking $marking
* @param Transition $transition
* @param WorkflowInterface $workflow
*/
public function __construct($subject, Marking $marking, Transition $transition, $workflow = null)
{