Skip to content

Commit d4f3b68

Browse files
Merge branch '4.3' into 4.4
* 4.3: 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
2 parents 7020f26 + b628210 commit d4f3b68

File tree

17 files changed

+253
-77
lines changed

17 files changed

+253
-77
lines changed

src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
passthru(sprintf('\\' === DIRECTORY_SEPARATOR ? 'rmdir /S /Q %s': 'rm -rf %s', "$PHPUNIT_VERSION_DIR.old"));
117117
}
118118
passthru("$COMPOSER create-project --no-install --prefer-dist --no-scripts --no-plugins --no-progress --ansi phpunit/phpunit $PHPUNIT_VERSION_DIR \"$PHPUNIT_VERSION.*\"");
119+
@copy("$PHPUNIT_VERSION_DIR/phpunit.xsd", 'phpunit.xsd');
119120
chdir("$PHPUNIT_VERSION_DIR");
120121
if ($SYMFONY_PHPUNIT_REMOVE) {
121122
passthru("$COMPOSER remove --no-update ".$SYMFONY_PHPUNIT_REMOVE);

src/Symfony/Bridge/Twig/TwigEngine.php

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Twig\Error\Error;
2222
use Twig\Error\LoaderError;
2323
use Twig\Loader\ExistsLoaderInterface;
24+
use Twig\Loader\SourceContextLoaderInterface;
2425
use Twig\Template;
2526

2627
/**
@@ -78,19 +79,24 @@ public function exists($name)
7879

7980
$loader = $this->environment->getLoader();
8081

81-
if ($loader instanceof ExistsLoaderInterface || method_exists($loader, 'exists')) {
82-
return $loader->exists((string) $name);
83-
}
82+
if (1 === Environment::MAJOR_VERSION && !$loader instanceof ExistsLoaderInterface) {
83+
try {
84+
// cast possible TemplateReferenceInterface to string because the
85+
// EngineInterface supports them but LoaderInterface does not
86+
if ($loader instanceof SourceContextLoaderInterface) {
87+
$loader->getSourceContext((string) $name);
88+
} else {
89+
$loader->getSource((string) $name);
90+
}
91+
92+
return true;
93+
} catch (LoaderError $e) {
94+
}
8495

85-
try {
86-
// cast possible TemplateReferenceInterface to string because the
87-
// EngineInterface supports them but LoaderInterface does not
88-
$loader->getSourceContext((string) $name)->getCode();
89-
} catch (LoaderError $e) {
9096
return false;
9197
}
9298

93-
return true;
99+
return $loader->exists((string) $name);
94100
}
95101

96102
/**

src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Twig\Environment;
1919
use Twig\Error\LoaderError;
2020
use Twig\Loader\ExistsLoaderInterface;
21+
use Twig\Loader\SourceContextLoaderInterface;
2122

2223
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', ExceptionController::class, \Symfony\Component\HttpKernel\Controller\ErrorController::class), E_USER_DEPRECATED);
2324

@@ -124,23 +125,28 @@ protected function findTemplate(Request $request, $format, $code, $showException
124125
return sprintf('@Twig/Exception/%s.html.twig', $showException ? 'exception_full' : $name);
125126
}
126127

127-
// to be removed when the minimum required version of Twig is >= 3.0
128+
// to be removed when the minimum required version of Twig is >= 2.0
128129
protected function templateExists($template)
129130
{
130131
$template = (string) $template;
131132

132133
$loader = $this->twig->getLoader();
133-
if ($loader instanceof ExistsLoaderInterface || method_exists($loader, 'exists')) {
134-
return $loader->exists($template);
135-
}
136134

137-
try {
138-
$loader->getSourceContext($template)->getCode();
135+
if (1 === Environment::MAJOR_VERSION && !$loader instanceof ExistsLoaderInterface) {
136+
try {
137+
if ($loader instanceof SourceContextLoaderInterface) {
138+
$loader->getSourceContext($template);
139+
} else {
140+
$loader->getSource($template);
141+
}
142+
143+
return true;
144+
} catch (LoaderError $e) {
145+
}
139146

140-
return true;
141-
} catch (LoaderError $e) {
147+
return false;
142148
}
143149

144-
return false;
150+
return $loader->exists($template);
145151
}
146152
}

src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.php

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Twig\Environment;
2020
use Twig\Error\LoaderError;
2121
use Twig\Loader\ExistsLoaderInterface;
22+
use Twig\Loader\SourceContextLoaderInterface;
2223

2324
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', ExceptionController::class, ExceptionPanelController::class), E_USER_DEPRECATED);
2425

@@ -120,17 +121,22 @@ protected function getTemplate()
120121
protected function templateExists($template)
121122
{
122123
$loader = $this->twig->getLoader();
123-
if ($loader instanceof ExistsLoaderInterface) {
124-
return $loader->exists($template);
125-
}
126124

127-
try {
128-
$loader->getSource($template);
125+
if (1 === Environment::MAJOR_VERSION && !$loader instanceof ExistsLoaderInterface) {
126+
try {
127+
if ($loader instanceof SourceContextLoaderInterface) {
128+
$loader->getSourceContext($template);
129+
} else {
130+
$loader->getSource($template);
131+
}
132+
133+
return true;
134+
} catch (LoaderError $e) {
135+
}
129136

130-
return true;
131-
} catch (LoaderError $e) {
137+
return false;
132138
}
133139

134-
return false;
140+
return $loader->exists($template);
135141
}
136142
}

src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -106,21 +106,22 @@ protected function templateExists($template/*, bool $triggerDeprecation = true *
106106
}
107107

108108
$loader = $this->twig->getLoader();
109-
if ($loader instanceof ExistsLoaderInterface) {
110-
return $loader->exists($template);
111-
}
112109

113-
try {
114-
if ($loader instanceof SourceContextLoaderInterface || method_exists($loader, 'getSourceContext')) {
115-
$loader->getSourceContext($template);
116-
} else {
117-
$loader->getSource($template);
110+
if (1 === Environment::MAJOR_VERSION && !$loader instanceof ExistsLoaderInterface) {
111+
try {
112+
if ($loader instanceof SourceContextLoaderInterface) {
113+
$loader->getSourceContext($template);
114+
} else {
115+
$loader->getSource($template);
116+
}
117+
118+
return true;
119+
} catch (LoaderError $e) {
118120
}
119121

120-
return true;
121-
} catch (LoaderError $e) {
122+
return false;
122123
}
123124

124-
return false;
125+
return $loader->exists($template);
125126
}
126127
}

src/Symfony/Bundle/WebProfilerBundle/Tests/Profiler/TemplateManagerTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ protected function mockTwigEnvironment()
110110
$loader = $this->getMockBuilder('Twig\Loader\LoaderInterface')->getMock();
111111
$loader->method('getSourceContext')->willReturn(new Source('source-code', 'source-name'));
112112
}
113+
113114
$this->twigEnvironment->expects($this->any())->method('getLoader')->willReturn($loader);
114115

115116
return $this->twigEnvironment;

src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ private function getTagVersions(array $tagsByKey, array &$invalidatedTags = [])
384384
continue;
385385
}
386386
$version -= $this->knownTagVersions[$tag][1];
387-
if ((0 !== $version && 1 !== $version) || $this->knownTagVersionsTtl > $now - $this->knownTagVersions[$tag][0]) {
387+
if ((0 !== $version && 1 !== $version) || $now - $this->knownTagVersions[$tag][0] >= $this->knownTagVersionsTtl) {
388388
// reuse previously fetched tag versions up to the ttl, unless we are storing items or a potential miss arises
389389
$fetchTagVersions = true;
390390
} else {

src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Cache\Tests\Adapter;
1313

1414
use PHPUnit\Framework\MockObject\MockObject;
15+
use Psr\Cache\CacheItemInterface;
1516
use Psr\Cache\CacheItemPoolInterface;
1617
use Symfony\Component\Cache\Adapter\AdapterInterface;
1718
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
@@ -67,6 +68,42 @@ public function testPrune()
6768
$this->assertFalse($cache->prune());
6869
}
6970

71+
public function testKnownTagVersionsTtl()
72+
{
73+
$itemsPool = new FilesystemAdapter('', 10);
74+
$tagsPool = $this
75+
->getMockBuilder(AdapterInterface::class)
76+
->getMock();
77+
78+
$pool = new TagAwareAdapter($itemsPool, $tagsPool, 10);
79+
80+
$item = $pool->getItem('foo');
81+
$item->tag(['baz']);
82+
$item->expiresAfter(100);
83+
84+
$tag = $this->getMockBuilder(CacheItemInterface::class)->getMock();
85+
$tag->expects(self::exactly(2))->method('get')->willReturn(10);
86+
87+
$tagsPool->expects(self::exactly(2))->method('getItems')->willReturn([
88+
'baz'.TagAwareAdapter::TAGS_PREFIX => $tag,
89+
]);
90+
91+
$pool->save($item);
92+
$this->assertTrue($pool->getItem('foo')->isHit());
93+
$this->assertTrue($pool->getItem('foo')->isHit());
94+
95+
sleep(20);
96+
97+
$this->assertTrue($pool->getItem('foo')->isHit());
98+
99+
sleep(5);
100+
101+
$this->assertTrue($pool->getItem('foo')->isHit());
102+
}
103+
104+
/**
105+
* @return MockObject|PruneableCacheInterface
106+
*/
70107
private function getPruneableMock(): AdapterInterface
71108
{
72109
$pruneable = $this->createMock([PruneableInterface::class, AdapterInterface::class]);

src/Symfony/Component/CssSelector/Tests/XPath/TranslatorTest.php

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public function testXmlLang($css, array $elementsId)
9898
$elements = $document->xpath($translator->cssToXPath($css));
9999
$this->assertCount(\count($elementsId), $elements);
100100
foreach ($elements as $element) {
101-
$this->assertTrue(\in_array($element->attributes()->id, $elementsId));
101+
$this->assertContains((string) $element->attributes()->id, $elementsId);
102102
}
103103
}
104104

@@ -116,7 +116,7 @@ public function testHtmlIds($css, array $elementsId)
116116
$this->assertCount(\count($elementsId), $elementsId);
117117
foreach ($elements as $element) {
118118
if (null !== $element->attributes()->id) {
119-
$this->assertTrue(\in_array($element->attributes()->id, $elementsId));
119+
$this->assertContains((string) $element->attributes()->id, $elementsId);
120120
}
121121
}
122122
libxml_clear_errors();
@@ -137,6 +137,33 @@ public function testHtmlShakespear($css, $count)
137137
$this->assertCount($count, $elements);
138138
}
139139

140+
public function testOnlyOfTypeFindsSingleChildrenOfGivenType()
141+
{
142+
$translator = new Translator();
143+
$translator->registerExtension(new HtmlExtension($translator));
144+
$document = new \DOMDocument();
145+
$document->loadHTML(<<<'HTML'
146+
<html>
147+
<body>
148+
<p>
149+
<span>A</span>
150+
</p>
151+
<p>
152+
<span>B</span>
153+
<span>C</span>
154+
</p>
155+
</body>
156+
</html>
157+
HTML
158+
);
159+
160+
$xpath = new \DOMXPath($document);
161+
$nodeList = $xpath->query($translator->cssToXPath('span:only-of-type'));
162+
163+
$this->assertSame(1, $nodeList->length);
164+
$this->assertSame('A', $nodeList->item(0)->textContent);
165+
}
166+
140167
public function getXpathLiteralTestData()
141168
{
142169
return [
@@ -175,7 +202,7 @@ public function getCssToXPathTestData()
175202
['e:first-of-type', '*/e[position() = 1]'],
176203
['e:last-of-type', '*/e[position() = last()]'],
177204
['e:only-child', "*/*[(name() = 'e') and (last() = 1)]"],
178-
['e:only-of-type', 'e[last() = 1]'],
205+
['e:only-of-type', 'e[count(preceding-sibling::e)=0 and count(following-sibling::e)=0]'],
179206
['e:empty', 'e[not(*) and not(string-length())]'],
180207
['e:EmPTY', 'e[not(*) and not(string-length())]'],
181208
['e:root', 'e[not(parent::*)]'],

src/Symfony/Component/CssSelector/XPath/Extension/PseudoClassExtension.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,13 @@ public function translateOnlyChild(XPathExpr $xpath): XPathExpr
105105
*/
106106
public function translateOnlyOfType(XPathExpr $xpath): XPathExpr
107107
{
108-
if ('*' === $xpath->getElement()) {
108+
$element = $xpath->getElement();
109+
110+
if ('*' === $element) {
109111
throw new ExpressionErrorException('"*:only-of-type" is not implemented.');
110112
}
111113

112-
return $xpath->addCondition('last() = 1');
114+
return $xpath->addCondition(sprintf('count(preceding-sibling::%s)=0 and count(following-sibling::%s)=0', $element, $element));
113115
}
114116

115117
public function translateEmpty(XPathExpr $xpath): XPathExpr

0 commit comments

Comments
 (0)