From 205abf343570940a1c406f7e051ff4d1688bc4f0 Mon Sep 17 00:00:00 2001 From: Swen van Zanten Date: Sun, 29 Sep 2019 23:19:44 +0200 Subject: [PATCH 1/7] [Cache] fix known tag versions ttl check --- .../Cache/Adapter/TagAwareAdapter.php | 2 +- .../Tests/Adapter/TagAwareAdapterTest.php | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php index 8e6024d846..cac8a0ea18 100644 --- a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php @@ -350,7 +350,7 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, R 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 { diff --git a/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php index 5bd2ffc287..c8677d5e28 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php @@ -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; @@ -160,6 +161,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 */ From 8814751b960d5c75b4d6ebb23548c7c938c2d65d Mon Sep 17 00:00:00 2001 From: Matthias Krauser Date: Fri, 27 Sep 2019 11:43:15 +0200 Subject: [PATCH 2/7] [Serializer] fix denormalization of string-arrays with only one element #33731 --- .../Normalizer/AbstractObjectNormalizer.php | 16 +++-- .../AbstractObjectNormalizerTest.php | 64 ++++++++++++++++--- 2 files changed, 63 insertions(+), 17 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index f2fb082b4b..6cb6ec5013 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -244,16 +244,18 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer return null; } - if ($type->isCollection() && null !== ($collectionValueType = $type->getCollectionValueType()) && Type::BUILTIN_TYPE_OBJECT === $collectionValueType->getBuiltinType()) { + $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 && 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().'[]'; - // Fix a collection that contains the only one element - // This is special to xml format only - if ('xml' === $format && !\is_int(key($data))) { - $data = [$data]; - } - if (null !== $collectionKeyType = $type->getCollectionKeyType()) { $context['key_type'] = $collectionKeyType; } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index f1eafe811f..4895526c45 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -121,16 +121,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 )); @@ -212,6 +250,12 @@ class AbstractObjectNormalizerWithMetadata extends AbstractObjectNormalizer } } +class StringCollection +{ + /** @var string[] */ + public $children; +} + class DummyCollection { /** @var DummyChild[] */ From fceb86bde6529d948232ce6ab60b924664e45993 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 30 Sep 2019 14:24:32 +0200 Subject: [PATCH 3/7] [Security/Http] fix parsing X509 emailAddress --- .../Security/Http/Firewall/X509AuthenticationListener.php | 2 +- .../Http/Tests/Firewall/X509AuthenticationListenerTest.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Security/Http/Firewall/X509AuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/X509AuthenticationListener.php index 76b1cad349..e3cfb9d80e 100644 --- a/src/Symfony/Component/Security/Http/Firewall/X509AuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/X509AuthenticationListener.php @@ -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]; } diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/X509AuthenticationListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/X509AuthenticationListenerTest.php index 9ada4b1b49..e35e685d5b 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/X509AuthenticationListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/X509AuthenticationListenerTest.php @@ -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() From 233dcb4b75093f14cdd9b8e3f22218c480b13261 Mon Sep 17 00:00:00 2001 From: Julien Falque Date: Mon, 30 Sep 2019 22:33:19 +0200 Subject: [PATCH 4/7] Copy phpunit.xsd to a predictable path --- src/Symfony/Bridge/PhpUnit/bin/simple-phpunit | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit index 02fd4de411..262fdd9dea 100755 --- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit +++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit @@ -78,6 +78,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); From 760354d533b01dad96b430b27ccdf03396fa9ed7 Mon Sep 17 00:00:00 2001 From: Roland Franssen Date: Fri, 14 Jun 2019 18:55:27 +0200 Subject: [PATCH 5/7] [Serializer] Add CsvEncoder tests for PHP 7.4 --- .../Serializer/Encoder/CsvEncoder.php | 6 ++- .../Tests/Encoder/CsvEncoderTest.php | 44 +++++++++++++++++-- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php b/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php index 79abf30ea5..239144d80f 100644 --- a/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php @@ -39,8 +39,12 @@ class CsvEncoder implements EncoderInterface, DecoderInterface * @param string $escapeChar * @param string $keySeparator */ - public function __construct($delimiter = ',', $enclosure = '"', $escapeChar = '\\', $keySeparator = '.') + public function __construct($delimiter = ',', $enclosure = '"', $escapeChar = '', $keySeparator = '.') { + if ('' === $escapeChar && \PHP_VERSION_ID < 70400) { + $escapeChar = '\\'; + } + $this->delimiter = $delimiter; $this->enclosure = $enclosure; $this->escapeChar = $escapeChar; diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php index 7b5131cb53..4faf42d2ec 100644 --- a/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php @@ -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->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() From c2a9bf08f13bbd761c12bacfc4d6e8f1f8634e0b Mon Sep 17 00:00:00 2001 From: Jakub Zalas Date: Mon, 30 Sep 2019 21:51:42 +0100 Subject: [PATCH 6/7] Fix the :only-of-type pseudo class selector --- .../Tests/XPath/TranslatorTest.php | 33 +++++++++++++++++-- .../XPath/Extension/PseudoClassExtension.php | 6 ++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/CssSelector/Tests/XPath/TranslatorTest.php b/src/Symfony/Component/CssSelector/Tests/XPath/TranslatorTest.php index 625096315d..a718fbd6f2 100644 --- a/src/Symfony/Component/CssSelector/Tests/XPath/TranslatorTest.php +++ b/src/Symfony/Component/CssSelector/Tests/XPath/TranslatorTest.php @@ -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' + + +

+ A +

+

+ B + C +

+ + +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::*)]'], diff --git a/src/Symfony/Component/CssSelector/XPath/Extension/PseudoClassExtension.php b/src/Symfony/Component/CssSelector/XPath/Extension/PseudoClassExtension.php index 27fe47f9a5..288a9e695e 100644 --- a/src/Symfony/Component/CssSelector/XPath/Extension/PseudoClassExtension.php +++ b/src/Symfony/Component/CssSelector/XPath/Extension/PseudoClassExtension.php @@ -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)); } /** From d7682fee6c45c085e529f17d40b56fd788866580 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Tue, 1 Oct 2019 15:11:47 +0200 Subject: [PATCH 7/7] Sync Twig templateExists behaviors --- src/Symfony/Bridge/Twig/TwigEngine.php | 24 ++++++++++------ .../Controller/ExceptionController.php | 28 +++++++++++-------- .../Controller/ExceptionController.php | 26 ++++++++++------- .../Profiler/TemplateManager.php | 23 +++++++-------- .../Tests/Profiler/TemplateManagerTest.php | 13 +++++++-- .../Fragment/HIncludeFragmentRenderer.php | 24 ++++++++-------- 6 files changed, 83 insertions(+), 55 deletions(-) diff --git a/src/Symfony/Bridge/Twig/TwigEngine.php b/src/Symfony/Bridge/Twig/TwigEngine.php index 4789f3f4d6..cfbb5d8f4e 100644 --- a/src/Symfony/Bridge/Twig/TwigEngine.php +++ b/src/Symfony/Bridge/Twig/TwigEngine.php @@ -19,6 +19,7 @@ use Twig\Environment; use Twig\Error\Error; use Twig\Error\LoaderError; use Twig\Loader\ExistsLoaderInterface; +use Twig\Loader\SourceContextLoaderInterface; use Twig\Template; /** @@ -74,19 +75,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 + if ($loader instanceof SourceContextLoaderInterface) { + $loader->getSourceContext((string) $name); + } else { + $loader->getSource((string) $name); + } + + return true; + } catch (LoaderError $e) { + } - 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; } - return true; + return $loader->exists((string) $name); } /** diff --git a/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php b/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php index da7f70a14d..22753af964 100644 --- a/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php +++ b/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php @@ -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,23 +121,28 @@ 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 { + if ($loader instanceof SourceContextLoaderInterface) { + $loader->getSourceContext($template); + } else { + $loader->getSource($template); + } + + return true; + } catch (LoaderError $e) { + } + + return false; } - try { - $loader->getSourceContext($template)->getCode(); - - return true; - } catch (LoaderError $e) { - } - - return false; + return $loader->exists($template); } } diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.php index d5b0f5e66b..e82144b874 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.php @@ -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,17 +119,22 @@ 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) { + } + + return false; } - try { - $loader->getSource($template); - - return true; - } catch (LoaderError $e) { - } - - return false; + return $loader->exists($template); } } diff --git a/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php b/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php index 5156152aa9..4f8da03cb5 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php @@ -117,21 +117,22 @@ class TemplateManager protected function templateExists($template) { $loader = $this->twig->getLoader(); - if ($loader instanceof ExistsLoaderInterface) { - return $loader->exists($template); - } - try { - if ($loader instanceof SourceContextLoaderInterface || method_exists($loader, 'getSourceContext')) { - $loader->getSourceContext($template); - } else { - $loader->getSource($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) { } - return true; - } catch (LoaderError $e) { + return false; } - return false; + return $loader->exists($template); } } diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/Profiler/TemplateManagerTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/Profiler/TemplateManagerTest.php index 1e5639a0b6..ab4a42fc0c 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Tests/Profiler/TemplateManagerTest.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/Profiler/TemplateManagerTest.php @@ -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. @@ -124,11 +126,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; diff --git a/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php b/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php index a0abc05881..f94a5c0760 100644 --- a/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php +++ b/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php @@ -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. @@ -137,22 +138,23 @@ class HIncludeFragmentRenderer extends RoutableFragmentRenderer } $loader = $this->templating->getLoader(); - if ($loader instanceof ExistsLoaderInterface || method_exists($loader, 'exists')) { - return $loader->exists($template); - } - try { - if (method_exists($loader, 'getSourceContext')) { - $loader->getSourceContext($template); - } else { - $loader->getSource($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) { } - return true; - } catch (LoaderError $e) { + return false; } - return false; + return $loader->exists($template); } /**