Merge branch '3.4' into 4.3
* 3.4: Sync Twig templateExists behaviors Fix the :only-of-type pseudo class selector [Serializer] Add CsvEncoder tests for PHP 7.4 Copy phpunit.xsd to a predictable path [Security/Http] fix parsing X509 emailAddress [Serializer] fix denormalization of string-arrays with only one element #33731 [Cache] fix known tag versions ttl check
This commit is contained in:
commit
b628210df7
@ -110,6 +110,7 @@ if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__
|
||||
passthru(sprintf('\\' === DIRECTORY_SEPARATOR ? 'rmdir /S /Q %s': 'rm -rf %s', "phpunit-$PHPUNIT_VERSION.old"));
|
||||
}
|
||||
passthru("$COMPOSER create-project --no-install --prefer-dist --no-scripts --no-plugins --no-progress --ansi phpunit/phpunit phpunit-$PHPUNIT_VERSION \"$PHPUNIT_VERSION.*\"");
|
||||
@copy("phpunit-$PHPUNIT_VERSION/phpunit.xsd", 'phpunit.xsd');
|
||||
chdir("phpunit-$PHPUNIT_VERSION");
|
||||
if ($SYMFONY_PHPUNIT_REMOVE) {
|
||||
passthru("$COMPOSER remove --no-update ".$SYMFONY_PHPUNIT_REMOVE);
|
||||
|
@ -21,6 +21,7 @@ use Twig\Environment;
|
||||
use Twig\Error\Error;
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Loader\ExistsLoaderInterface;
|
||||
use Twig\Loader\SourceContextLoaderInterface;
|
||||
use Twig\Template;
|
||||
|
||||
/**
|
||||
@ -78,19 +79,24 @@ class TwigEngine implements EngineInterface, StreamingEngineInterface
|
||||
|
||||
$loader = $this->environment->getLoader();
|
||||
|
||||
if ($loader instanceof ExistsLoaderInterface || method_exists($loader, 'exists')) {
|
||||
return $loader->exists((string) $name);
|
||||
}
|
||||
|
||||
if (1 === Environment::MAJOR_VERSION && !$loader instanceof ExistsLoaderInterface) {
|
||||
try {
|
||||
// cast possible TemplateReferenceInterface to string because the
|
||||
// EngineInterface supports them but LoaderInterface does not
|
||||
$loader->getSourceContext((string) $name)->getCode();
|
||||
} catch (LoaderError $e) {
|
||||
return false;
|
||||
if ($loader instanceof SourceContextLoaderInterface) {
|
||||
$loader->getSourceContext((string) $name);
|
||||
} else {
|
||||
$loader->getSource((string) $name);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (LoaderError $e) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return $loader->exists((string) $name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -18,6 +18,7 @@ use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
|
||||
use Twig\Environment;
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Loader\ExistsLoaderInterface;
|
||||
use Twig\Loader\SourceContextLoaderInterface;
|
||||
|
||||
/**
|
||||
* ExceptionController renders error or exception pages for a given
|
||||
@ -120,18 +121,20 @@ class ExceptionController
|
||||
return sprintf('@Twig/Exception/%s.html.twig', $showException ? 'exception_full' : $name);
|
||||
}
|
||||
|
||||
// to be removed when the minimum required version of Twig is >= 3.0
|
||||
// to be removed when the minimum required version of Twig is >= 2.0
|
||||
protected function templateExists($template)
|
||||
{
|
||||
$template = (string) $template;
|
||||
|
||||
$loader = $this->twig->getLoader();
|
||||
if ($loader instanceof ExistsLoaderInterface || method_exists($loader, 'exists')) {
|
||||
return $loader->exists($template);
|
||||
}
|
||||
|
||||
if (1 === Environment::MAJOR_VERSION && !$loader instanceof ExistsLoaderInterface) {
|
||||
try {
|
||||
$loader->getSourceContext($template)->getCode();
|
||||
if ($loader instanceof SourceContextLoaderInterface) {
|
||||
$loader->getSourceContext($template);
|
||||
} else {
|
||||
$loader->getSource($template);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (LoaderError $e) {
|
||||
@ -139,4 +142,7 @@ class ExceptionController
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return $loader->exists($template);
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ use Symfony\Component\HttpKernel\Profiler\Profiler;
|
||||
use Twig\Environment;
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Loader\ExistsLoaderInterface;
|
||||
use Twig\Loader\SourceContextLoaderInterface;
|
||||
|
||||
/**
|
||||
* ExceptionController.
|
||||
@ -118,12 +119,14 @@ class ExceptionController
|
||||
protected function templateExists($template)
|
||||
{
|
||||
$loader = $this->twig->getLoader();
|
||||
if ($loader instanceof ExistsLoaderInterface) {
|
||||
return $loader->exists($template);
|
||||
}
|
||||
|
||||
if (1 === Environment::MAJOR_VERSION && !$loader instanceof ExistsLoaderInterface) {
|
||||
try {
|
||||
if ($loader instanceof SourceContextLoaderInterface) {
|
||||
$loader->getSourceContext($template);
|
||||
} else {
|
||||
$loader->getSource($template);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (LoaderError $e) {
|
||||
@ -131,4 +134,7 @@ class ExceptionController
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return $loader->exists($template);
|
||||
}
|
||||
}
|
||||
|
@ -99,12 +99,10 @@ class TemplateManager
|
||||
protected function templateExists($template)
|
||||
{
|
||||
$loader = $this->twig->getLoader();
|
||||
if ($loader instanceof ExistsLoaderInterface) {
|
||||
return $loader->exists($template);
|
||||
}
|
||||
|
||||
if (1 === Environment::MAJOR_VERSION && !$loader instanceof ExistsLoaderInterface) {
|
||||
try {
|
||||
if ($loader instanceof SourceContextLoaderInterface || method_exists($loader, 'getSourceContext')) {
|
||||
if ($loader instanceof SourceContextLoaderInterface) {
|
||||
$loader->getSourceContext($template);
|
||||
} else {
|
||||
$loader->getSource($template);
|
||||
@ -116,4 +114,7 @@ class TemplateManager
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return $loader->exists($template);
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,8 @@ use Symfony\Bundle\WebProfilerBundle\Profiler\TemplateManager;
|
||||
use Symfony\Bundle\WebProfilerBundle\Tests\TestCase;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profile;
|
||||
use Twig\Environment;
|
||||
use Twig\Loader\LoaderInterface;
|
||||
use Twig\Loader\SourceContextLoaderInterface;
|
||||
|
||||
/**
|
||||
* Test for TemplateManager class.
|
||||
@ -107,11 +109,16 @@ class TemplateManagerTest extends TestCase
|
||||
->method('loadTemplate')
|
||||
->willReturn('loadedTemplate');
|
||||
|
||||
if (interface_exists('Twig\Loader\SourceContextLoaderInterface')) {
|
||||
$loader = $this->getMockBuilder('Twig\Loader\SourceContextLoaderInterface')->getMock();
|
||||
if (Environment::MAJOR_VERSION > 1) {
|
||||
$loader = $this->createMock(LoaderInterface::class);
|
||||
$loader
|
||||
->expects($this->any())
|
||||
->method('exists')
|
||||
->willReturn(true);
|
||||
} else {
|
||||
$loader = $this->getMockBuilder('Twig\Loader\LoaderInterface')->getMock();
|
||||
$loader = $this->createMock(SourceContextLoaderInterface::class);
|
||||
}
|
||||
|
||||
$this->twigEnvironment->expects($this->any())->method('getLoader')->willReturn($loader);
|
||||
|
||||
return $this->twigEnvironment;
|
||||
|
@ -354,7 +354,7 @@ class TagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterfac
|
||||
continue;
|
||||
}
|
||||
$version -= $this->knownTagVersions[$tag][1];
|
||||
if ((0 !== $version && 1 !== $version) || $this->knownTagVersionsTtl > $now - $this->knownTagVersions[$tag][0]) {
|
||||
if ((0 !== $version && 1 !== $version) || $now - $this->knownTagVersions[$tag][0] >= $this->knownTagVersionsTtl) {
|
||||
// reuse previously fetched tag versions up to the ttl, unless we are storing items or a potential miss arises
|
||||
$fetchTagVersions = true;
|
||||
} else {
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace Symfony\Component\Cache\Tests\Adapter;
|
||||
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Symfony\Component\Cache\Adapter\AdapterInterface;
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
use Symfony\Component\Cache\Adapter\TagAwareAdapter;
|
||||
@ -65,6 +66,39 @@ class TagAwareAdapterTest extends AdapterTestCase
|
||||
$this->assertFalse($cache->prune());
|
||||
}
|
||||
|
||||
public function testKnownTagVersionsTtl()
|
||||
{
|
||||
$itemsPool = new FilesystemAdapter('', 10);
|
||||
$tagsPool = $this
|
||||
->getMockBuilder(AdapterInterface::class)
|
||||
->getMock();
|
||||
|
||||
$pool = new TagAwareAdapter($itemsPool, $tagsPool, 10);
|
||||
|
||||
$item = $pool->getItem('foo');
|
||||
$item->tag(['baz']);
|
||||
$item->expiresAfter(100);
|
||||
|
||||
$tag = $this->getMockBuilder(CacheItemInterface::class)->getMock();
|
||||
$tag->expects(self::exactly(2))->method('get')->willReturn(10);
|
||||
|
||||
$tagsPool->expects(self::exactly(2))->method('getItems')->willReturn([
|
||||
'baz'.TagAwareAdapter::TAGS_PREFIX => $tag,
|
||||
]);
|
||||
|
||||
$pool->save($item);
|
||||
$this->assertTrue($pool->getItem('foo')->isHit());
|
||||
$this->assertTrue($pool->getItem('foo')->isHit());
|
||||
|
||||
sleep(20);
|
||||
|
||||
$this->assertTrue($pool->getItem('foo')->isHit());
|
||||
|
||||
sleep(5);
|
||||
|
||||
$this->assertTrue($pool->getItem('foo')->isHit());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MockObject|PruneableCacheInterface
|
||||
*/
|
||||
|
@ -98,7 +98,7 @@ class TranslatorTest extends TestCase
|
||||
$elements = $document->xpath($translator->cssToXPath($css));
|
||||
$this->assertCount(\count($elementsId), $elements);
|
||||
foreach ($elements as $element) {
|
||||
$this->assertTrue(\in_array($element->attributes()->id, $elementsId));
|
||||
$this->assertContains((string) $element->attributes()->id, $elementsId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,7 +116,7 @@ class TranslatorTest extends TestCase
|
||||
$this->assertCount(\count($elementsId), $elementsId);
|
||||
foreach ($elements as $element) {
|
||||
if (null !== $element->attributes()->id) {
|
||||
$this->assertTrue(\in_array($element->attributes()->id, $elementsId));
|
||||
$this->assertContains((string) $element->attributes()->id, $elementsId);
|
||||
}
|
||||
}
|
||||
libxml_clear_errors();
|
||||
@ -137,6 +137,33 @@ class TranslatorTest extends TestCase
|
||||
$this->assertCount($count, $elements);
|
||||
}
|
||||
|
||||
public function testOnlyOfTypeFindsSingleChildrenOfGivenType()
|
||||
{
|
||||
$translator = new Translator();
|
||||
$translator->registerExtension(new HtmlExtension($translator));
|
||||
$document = new \DOMDocument();
|
||||
$document->loadHTML(<<<'HTML'
|
||||
<html>
|
||||
<body>
|
||||
<p>
|
||||
<span>A</span>
|
||||
</p>
|
||||
<p>
|
||||
<span>B</span>
|
||||
<span>C</span>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
HTML
|
||||
);
|
||||
|
||||
$xpath = new \DOMXPath($document);
|
||||
$nodeList = $xpath->query($translator->cssToXPath('span:only-of-type'));
|
||||
|
||||
$this->assertSame(1, $nodeList->length);
|
||||
$this->assertSame('A', $nodeList->item(0)->textContent);
|
||||
}
|
||||
|
||||
public function getXpathLiteralTestData()
|
||||
{
|
||||
return [
|
||||
@ -175,7 +202,7 @@ class TranslatorTest extends TestCase
|
||||
['e:first-of-type', '*/e[position() = 1]'],
|
||||
['e:last-of-type', '*/e[position() = last()]'],
|
||||
['e:only-child', "*/*[(name() = 'e') and (last() = 1)]"],
|
||||
['e:only-of-type', 'e[last() = 1]'],
|
||||
['e:only-of-type', 'e[count(preceding-sibling::e)=0 and count(following-sibling::e)=0]'],
|
||||
['e:empty', 'e[not(*) and not(string-length())]'],
|
||||
['e:EmPTY', 'e[not(*) and not(string-length())]'],
|
||||
['e:root', 'e[not(parent::*)]'],
|
||||
|
@ -123,11 +123,13 @@ class PseudoClassExtension extends AbstractExtension
|
||||
*/
|
||||
public function translateOnlyOfType(XPathExpr $xpath)
|
||||
{
|
||||
if ('*' === $xpath->getElement()) {
|
||||
$element = $xpath->getElement();
|
||||
|
||||
if ('*' === $element) {
|
||||
throw new ExpressionErrorException('"*:only-of-type" is not implemented.');
|
||||
}
|
||||
|
||||
return $xpath->addCondition('last() = 1');
|
||||
return $xpath->addCondition(sprintf('count(preceding-sibling::%s)=0 and count(following-sibling::%s)=0', $element, $element));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -19,6 +19,7 @@ use Symfony\Component\Templating\EngineInterface;
|
||||
use Twig\Environment;
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Loader\ExistsLoaderInterface;
|
||||
use Twig\Loader\SourceContextLoaderInterface;
|
||||
|
||||
/**
|
||||
* Implements the Hinclude rendering strategy.
|
||||
@ -135,12 +136,10 @@ class HIncludeFragmentRenderer extends RoutableFragmentRenderer
|
||||
}
|
||||
|
||||
$loader = $this->templating->getLoader();
|
||||
if ($loader instanceof ExistsLoaderInterface || method_exists($loader, 'exists')) {
|
||||
return $loader->exists($template);
|
||||
}
|
||||
|
||||
if (1 === Environment::MAJOR_VERSION && !$loader instanceof ExistsLoaderInterface) {
|
||||
try {
|
||||
if (method_exists($loader, 'getSourceContext')) {
|
||||
if ($loader instanceof SourceContextLoaderInterface) {
|
||||
$loader->getSourceContext($template);
|
||||
} else {
|
||||
$loader->getSource($template);
|
||||
@ -153,6 +152,9 @@ class HIncludeFragmentRenderer extends RoutableFragmentRenderer
|
||||
return false;
|
||||
}
|
||||
|
||||
return $loader->exists($template);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -46,7 +46,7 @@ class X509AuthenticationListener extends AbstractPreAuthenticatedListener
|
||||
$user = $request->server->get($this->userKey);
|
||||
} elseif (
|
||||
$request->server->has($this->credentialKey)
|
||||
&& preg_match('#emailAddress=(.+\@.+\.[^,/]+)($|,|/)#', $request->server->get($this->credentialKey), $matches)
|
||||
&& preg_match('#emailAddress=([^,/@]++@[^,/]++)#', $request->server->get($this->credentialKey), $matches)
|
||||
) {
|
||||
$user = $matches[1];
|
||||
}
|
||||
|
@ -81,6 +81,7 @@ class X509AuthenticationListenerTest extends TestCase
|
||||
yield ['cert+something@example.com', 'CN=Sample certificate DN,emailAddress=cert+something@example.com'];
|
||||
yield ['cert+something@example.com', 'emailAddress=cert+something@example.com,CN=Sample certificate DN'];
|
||||
yield ['cert+something@example.com', 'emailAddress=cert+something@example.com'];
|
||||
yield ['firstname.lastname@mycompany.co.uk', 'emailAddress=firstname.lastname@mycompany.co.uk,CN=Firstname.Lastname,OU=london,OU=company design and engineering,OU=Issuer London,OU=Roaming,OU=Interactive,OU=Users,OU=Standard,OU=Business,DC=england,DC=core,DC=company,DC=co,DC=uk'];
|
||||
}
|
||||
|
||||
public function testGetPreAuthenticatedDataNoData()
|
||||
|
@ -45,8 +45,12 @@ class CsvEncoder implements EncoderInterface, DecoderInterface
|
||||
/**
|
||||
* @param array $defaultContext
|
||||
*/
|
||||
public function __construct($defaultContext = [], string $enclosure = '"', string $escapeChar = '\\', string $keySeparator = '.', bool $escapeFormulas = false)
|
||||
public function __construct($defaultContext = [], string $enclosure = '"', string $escapeChar = '', string $keySeparator = '.', bool $escapeFormulas = false)
|
||||
{
|
||||
if ('' === $escapeChar && \PHP_VERSION_ID < 70400) {
|
||||
$escapeChar = '\\';
|
||||
}
|
||||
|
||||
if (!\is_array($defaultContext)) {
|
||||
@trigger_error('Passing configuration options directly to the constructor is deprecated since Symfony 4.2, use the default context instead.', E_USER_DEPRECATED);
|
||||
|
||||
|
@ -397,16 +397,18 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($type->isCollection() && null !== ($collectionValueType = $type->getCollectionValueType()) && Type::BUILTIN_TYPE_OBJECT === $collectionValueType->getBuiltinType()) {
|
||||
$builtinType = Type::BUILTIN_TYPE_OBJECT;
|
||||
$class = $collectionValueType->getClassName().'[]';
|
||||
$collectionValueType = $type->isCollection() ? $type->getCollectionValueType() : null;
|
||||
|
||||
// Fix a collection that contains the only one element
|
||||
// This is special to xml format only
|
||||
if ('xml' === $format && !\is_int(key($data))) {
|
||||
if ('xml' === $format && null !== $collectionValueType && (!\is_array($data) || !\is_int(key($data)))) {
|
||||
$data = [$data];
|
||||
}
|
||||
|
||||
if (null !== $collectionValueType && Type::BUILTIN_TYPE_OBJECT === $collectionValueType->getBuiltinType()) {
|
||||
$builtinType = Type::BUILTIN_TYPE_OBJECT;
|
||||
$class = $collectionValueType->getClassName().'[]';
|
||||
|
||||
if (null !== $collectionKeyType = $type->getCollectionKeyType()) {
|
||||
$context['key_type'] = $collectionKeyType;
|
||||
}
|
||||
|
@ -36,15 +36,51 @@ class CsvEncoderTest extends TestCase
|
||||
'int' => 2,
|
||||
'false' => false,
|
||||
'true' => true,
|
||||
'int_one' => 1,
|
||||
'string_one' => '1',
|
||||
];
|
||||
|
||||
// Check that true and false are appropriately handled
|
||||
$this->assertEquals(<<<'CSV'
|
||||
string,int,false,true
|
||||
foo,2,0,1
|
||||
$this->assertSame($csv = <<<'CSV'
|
||||
string,int,false,true,int_one,string_one
|
||||
foo,2,0,1,1,1
|
||||
|
||||
CSV
|
||||
, $this->encoder->encode($data, 'csv'));
|
||||
|
||||
$this->assertSame([
|
||||
'string' => 'foo',
|
||||
'int' => '2',
|
||||
'false' => '0',
|
||||
'true' => '1',
|
||||
'int_one' => '1',
|
||||
'string_one' => '1',
|
||||
], $this->encoder->decode($csv, 'csv'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 7.4
|
||||
*/
|
||||
public function testDoubleQuotesAndSlashes()
|
||||
{
|
||||
$this->assertSame($csv = <<<'CSV'
|
||||
0,1,2,3,4,5
|
||||
,"""","foo""","\""",\,foo\
|
||||
|
||||
CSV
|
||||
, $this->encoder->encode($data = ['', '"', 'foo"', '\\"', '\\', 'foo\\'], 'csv'));
|
||||
|
||||
$this->assertSame($data, $this->encoder->decode($csv, 'csv'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 7.4
|
||||
*/
|
||||
public function testSingleSlash()
|
||||
{
|
||||
$this->assertSame($csv = "0\n\\\n", $this->encoder->encode($data = ['\\'], 'csv'));
|
||||
$this->assertSame($data, $this->encoder->decode($csv, 'csv'));
|
||||
$this->assertSame($data, $this->encoder->decode(trim($csv), 'csv'));
|
||||
}
|
||||
|
||||
public function testSupportEncoding()
|
||||
|
@ -132,16 +132,54 @@ class AbstractObjectNormalizerTest extends TestCase
|
||||
$extractor = $this->getMockBuilder(PhpDocExtractor::class)->getMock();
|
||||
$extractor->method('getTypes')
|
||||
->will($this->onConsecutiveCalls(
|
||||
[
|
||||
new Type(
|
||||
'array',
|
||||
false,
|
||||
null,
|
||||
true,
|
||||
new Type('int'),
|
||||
new Type('object', false, DummyChild::class)
|
||||
),
|
||||
],
|
||||
[new Type('array', false, null, true, new Type('int'), new Type('object', false, DummyChild::class))],
|
||||
null
|
||||
));
|
||||
|
||||
$denormalizer = new AbstractObjectNormalizerCollectionDummy(null, null, $extractor);
|
||||
$arrayDenormalizer = new ArrayDenormalizerDummy();
|
||||
$serializer = new SerializerCollectionDummy([$arrayDenormalizer, $denormalizer]);
|
||||
$arrayDenormalizer->setSerializer($serializer);
|
||||
$denormalizer->setSerializer($serializer);
|
||||
|
||||
return $denormalizer;
|
||||
}
|
||||
|
||||
public function testDenormalizeStringCollectionDecodedFromXmlWithOneChild()
|
||||
{
|
||||
$denormalizer = $this->getDenormalizerForStringCollection();
|
||||
|
||||
// if an xml-node can have children which should be deserialized as string[]
|
||||
// and only one child exists
|
||||
$stringCollection = $denormalizer->denormalize(['children' => 'foo'], StringCollection::class, 'xml');
|
||||
|
||||
$this->assertInstanceOf(StringCollection::class, $stringCollection);
|
||||
$this->assertIsArray($stringCollection->children);
|
||||
$this->assertCount(1, $stringCollection->children);
|
||||
$this->assertEquals('foo', $stringCollection->children[0]);
|
||||
}
|
||||
|
||||
public function testDenormalizeStringCollectionDecodedFromXmlWithTwoChildren()
|
||||
{
|
||||
$denormalizer = $this->getDenormalizerForStringCollection();
|
||||
|
||||
// if an xml-node can have children which should be deserialized as string[]
|
||||
// and only one child exists
|
||||
$stringCollection = $denormalizer->denormalize(['children' => ['foo', 'bar']], StringCollection::class, 'xml');
|
||||
|
||||
$this->assertInstanceOf(StringCollection::class, $stringCollection);
|
||||
$this->assertIsArray($stringCollection->children);
|
||||
$this->assertCount(2, $stringCollection->children);
|
||||
$this->assertEquals('foo', $stringCollection->children[0]);
|
||||
$this->assertEquals('bar', $stringCollection->children[1]);
|
||||
}
|
||||
|
||||
private function getDenormalizerForStringCollection()
|
||||
{
|
||||
$extractor = $this->getMockBuilder(PhpDocExtractor::class)->getMock();
|
||||
$extractor->method('getTypes')
|
||||
->will($this->onConsecutiveCalls(
|
||||
[new Type('array', false, null, true, new Type('int'), new Type('string'))],
|
||||
null
|
||||
));
|
||||
|
||||
@ -258,6 +296,12 @@ class AbstractObjectNormalizerWithMetadata extends AbstractObjectNormalizer
|
||||
}
|
||||
}
|
||||
|
||||
class StringCollection
|
||||
{
|
||||
/** @var string[] */
|
||||
public $children;
|
||||
}
|
||||
|
||||
class DummyCollection
|
||||
{
|
||||
/** @var DummyChild[] */
|
||||
|
Reference in New Issue
Block a user