Skip to content

Commit 4198ad3

Browse files
Merge branch '3.4'
* 3.4: [TwigBridge] fix BC for FormExtension if renderer is FormRenderer [Form] Fix 5.5 compatibility for ResizeFormListener [BrowserKit] Handle deprecations triggered in insulated requests [Bridge\PhpUnit] Handle deprecations triggered in separate processes Fix LogLevel::DEBUG as min level [Validator] added magic method __isset() to File Constraint class Support array of types in allowed type [DI] Fix possible incorrect php-code when dumped strings contains newlines [Translation] minor: remove unused variable in test added ability to handle parent classes for PropertyNormalizer replace parameters in dummy identity translator never match invalid IP addresses
2 parents b968e82 + 2c18da7 commit 4198ad3

File tree

23 files changed

+523
-46
lines changed

23 files changed

+523
-46
lines changed

src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,20 @@ public static function register($mode = 0)
238238
}
239239
}
240240

241+
public static function collectDeprecations($outputFile)
242+
{
243+
$deprecations = array();
244+
$previousErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = array()) use (&$deprecations, &$previousErrorHandler) {
245+
if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) {
246+
return $previousErrorHandler ? $previousErrorHandler($type, $msg, $file, $line, $context) : false;
247+
}
248+
$deprecations[] = array(error_reporting(), $msg);
249+
});
250+
register_shutdown_function(function () use ($outputFile, &$deprecations) {
251+
file_put_contents($outputFile, serialize($deprecations));
252+
});
253+
}
254+
241255
private static function hasColorSupport()
242256
{
243257
if ('\\' === DIRECTORY_SEPARATOR) {

src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class SymfonyTestsListenerTrait
3939
private $testsWithWarnings;
4040
private $reportUselessTests;
4141
private $error;
42+
private $runsInSeparateProcess = false;
4243

4344
/**
4445
* @param array $mockedNamespaces List of namespaces, indexed by mocked features (time-sensitive or dns-sensitive)
@@ -174,6 +175,12 @@ public function startTest($test)
174175
$this->reportUselessTests = $test->getTestResultObject()->isStrictAboutTestsThatDoNotTestAnything();
175176
}
176177

178+
// This event is triggered before the test is re-run in isolation
179+
if ($this->willBeIsolated($test)) {
180+
$this->runsInSeparateProcess = tempnam(sys_get_temp_dir(), 'deprec');
181+
putenv('SYMFONY_DEPRECATIONS_SERIALIZE='.$this->runsInSeparateProcess);
182+
}
183+
177184
if (class_exists('PHPUnit_Util_Blacklist', false)) {
178185
$Test = 'PHPUnit_Util_Test';
179186
$AssertionFailedError = 'PHPUnit_Framework_AssertionFailedError';
@@ -183,12 +190,14 @@ public function startTest($test)
183190
}
184191
$groups = $Test::getGroups(get_class($test), $test->getName(false));
185192

186-
if (in_array('time-sensitive', $groups, true)) {
187-
ClockMock::register(get_class($test));
188-
ClockMock::withClockMock(true);
189-
}
190-
if (in_array('dns-sensitive', $groups, true)) {
191-
DnsMock::register(get_class($test));
193+
if (!$this->runsInSeparateProcess) {
194+
if (in_array('time-sensitive', $groups, true)) {
195+
ClockMock::register(get_class($test));
196+
ClockMock::withClockMock(true);
197+
}
198+
if (in_array('dns-sensitive', $groups, true)) {
199+
DnsMock::register(get_class($test));
200+
}
192201
}
193202

194203
$annotations = $Test::parseTestMethodAnnotations(get_class($test), $test->getName(false));
@@ -236,15 +245,20 @@ public function endTest($test, $time)
236245
$this->reportUselessTests = null;
237246
}
238247

239-
$errored = false;
248+
if ($errored = null !== $this->error) {
249+
$test->getTestResultObject()->addError($test, $this->error, 0);
250+
$this->error = null;
251+
}
240252

241-
if (null !== $this->error) {
242-
if ($BaseTestRunner::STATUS_PASSED === $test->getStatus()) {
243-
$test->getTestResultObject()->addError($test, $this->error, 0);
244-
$errored = true;
253+
if ($this->runsInSeparateProcess) {
254+
foreach (unserialize(file_get_contents($this->runsInSeparateProcess)) as $deprecation) {
255+
if ($deprecation[0]) {
256+
trigger_error($deprecation[1], E_USER_DEPRECATED);
257+
} else {
258+
@trigger_error($deprecation[1], E_USER_DEPRECATED);
259+
}
245260
}
246-
247-
$this->error = null;
261+
$this->runsInSeparateProcess = false;
248262
}
249263

250264
if ($this->expectedDeprecations) {
@@ -268,7 +282,7 @@ public function endTest($test, $time)
268282
$this->expectedDeprecations = $this->gatheredDeprecations = array();
269283
$this->previousErrorHandler = null;
270284
}
271-
if (-2 < $this->state && ($test instanceof \PHPUnit_Framework_TestCase || $test instanceof TestCase)) {
285+
if (!$this->runsInSeparateProcess && -2 < $this->state && ($test instanceof \PHPUnit_Framework_TestCase || $test instanceof TestCase)) {
272286
if (in_array('time-sensitive', $groups, true)) {
273287
ClockMock::withClockMock(false);
274288
}
@@ -290,4 +304,21 @@ public function handleError($type, $msg, $file, $line, $context = array())
290304
}
291305
$this->gatheredDeprecations[] = $msg;
292306
}
307+
308+
/**
309+
* @param Test $test
310+
*
311+
* @return bool
312+
*/
313+
private function willBeIsolated($test)
314+
{
315+
if ($test->isInIsolation()) {
316+
return false;
317+
}
318+
319+
$r = new \ReflectionProperty($test, 'runTestInSeparateProcess');
320+
$r->setAccessible(true);
321+
322+
return $r->getValue($test);
323+
}
293324
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Symfony\Bridge\PhpUnit\Tests;
4+
5+
use PHPUnit\Framework\TestCase;
6+
7+
/**
8+
* Don't remove this test case, it tests the legacy group.
9+
*
10+
* @group legacy
11+
*
12+
* @runTestsInSeparateProcesses
13+
*/
14+
class ProcessIsolationTest extends TestCase
15+
{
16+
/**
17+
* @expectedDeprecation Test abc
18+
*/
19+
public function testIsolation()
20+
{
21+
@trigger_error('Test abc', E_USER_DEPRECATED);
22+
}
23+
}

src/Symfony/Bridge/PhpUnit/bootstrap.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414

1515
// Detect if we're loaded by an actual run of phpunit
1616
if (!defined('PHPUNIT_COMPOSER_INSTALL') && !class_exists('PHPUnit_TextUI_Command', false) && !class_exists('PHPUnit\TextUI\Command', false)) {
17+
if ($ser = getenv('SYMFONY_DEPRECATIONS_SERIALIZE')) {
18+
DeprecationErrorHandler::collectDeprecations($ser);
19+
}
20+
1721
return;
1822
}
1923

src/Symfony/Bridge/Twig/Extension/TranslationExtension.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public function getTranslationNodeVisitor()
9595
public function trans($message, array $arguments = array(), $domain = null, $locale = null)
9696
{
9797
if (null === $this->translator) {
98-
return $message;
98+
return strtr($message, $arguments);
9999
}
100100

101101
return $this->translator->trans($message, $arguments, $domain, $locale);
@@ -104,7 +104,7 @@ public function trans($message, array $arguments = array(), $domain = null, $loc
104104
public function transchoice($message, $count, array $arguments = array(), $domain = null, $locale = null)
105105
{
106106
if (null === $this->translator) {
107-
return $message;
107+
return strtr($message, $arguments);
108108
}
109109

110110
return $this->translator->transChoice($message, $count, array_merge(array('%count%' => $count), $arguments), $domain, $locale);
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Twig\Tests\Extension;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bridge\Twig\Extension\FormExtension;
16+
use Symfony\Bridge\Twig\Form\TwigRendererInterface;
17+
use Symfony\Component\DependencyInjection\ContainerInterface;
18+
use Symfony\Component\Form\FormRendererInterface;
19+
use Twig\Environment;
20+
21+
/**
22+
* @group legacy
23+
*/
24+
class FormExtensionTest extends TestCase
25+
{
26+
/**
27+
* @dataProvider rendererDataProvider
28+
*/
29+
public function testInitRuntimeAndAccessRenderer($rendererConstructor, $expectedAccessedRenderer)
30+
{
31+
$extension = new FormExtension($rendererConstructor);
32+
$extension->initRuntime($this->getMockBuilder(Environment::class)->disableOriginalConstructor()->getMock());
33+
$this->assertSame($expectedAccessedRenderer, $extension->renderer);
34+
}
35+
36+
/**
37+
* @dataProvider rendererDataProvider
38+
*/
39+
public function testAccessRendererAndInitRuntime($rendererConstructor, $expectedAccessedRenderer)
40+
{
41+
$extension = new FormExtension($rendererConstructor);
42+
$this->assertSame($expectedAccessedRenderer, $extension->renderer);
43+
$extension->initRuntime($this->getMockBuilder(Environment::class)->disableOriginalConstructor()->getMock());
44+
}
45+
46+
public function rendererDataProvider()
47+
{
48+
$twigRenderer = $this->getMockBuilder(TwigRendererInterface::class)->getMock();
49+
$twigRenderer->expects($this->once())
50+
->method('setEnvironment');
51+
52+
yield array($twigRenderer, $twigRenderer);
53+
54+
$twigRenderer = $this->getMockBuilder(TwigRendererInterface::class)->getMock();
55+
$twigRenderer->expects($this->once())
56+
->method('setEnvironment');
57+
58+
$container = $this->getMockBuilder(ContainerInterface::class)->getMock();
59+
$container->expects($this->once())
60+
->method('get')
61+
->with('service_id')
62+
->willReturn($twigRenderer);
63+
64+
yield array(array($container, 'service_id'), $twigRenderer);
65+
66+
$formRenderer = $this->getMockBuilder(FormRendererInterface::class)->getMock();
67+
68+
$container = $this->getMockBuilder(ContainerInterface::class)->getMock();
69+
$container->expects($this->once())
70+
->method('get')
71+
->with('service_id')
72+
->willReturn($formRenderer);
73+
74+
yield array(array($container, 'service_id'), $formRenderer);
75+
}
76+
}

src/Symfony/Bridge/Twig/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"require-dev": {
2323
"fig/link-util": "^1.0",
2424
"symfony/asset": "~3.4|~4.0",
25+
"symfony/dependency-injection": "~3.4|~4.0",
2526
"symfony/finder": "~3.4|~4.0",
2627
"symfony/form": "~3.4|~4.0",
2728
"symfony/http-foundation": "~3.4|~4.0",

src/Symfony/Component/BrowserKit/Client.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,9 +346,23 @@ public function request($method, $uri, array $parameters = array(), array $files
346346
*/
347347
protected function doRequestInProcess($request)
348348
{
349+
$deprecationsFile = tempnam(sys_get_temp_dir(), 'deprec');
350+
putenv('SYMFONY_DEPRECATIONS_SERIALIZE='.$deprecationsFile);
349351
$process = new PhpProcess($this->getScript($request), null, null);
350352
$process->run();
351353

354+
if (file_exists($deprecationsFile)) {
355+
$deprecations = file_get_contents($deprecationsFile);
356+
unlink($deprecationsFile);
357+
foreach ($deprecations ? unserialize($deprecations) : array() as $deprecation) {
358+
if ($deprecation[0]) {
359+
trigger_error($deprecation[1], E_USER_DEPRECATED);
360+
} else {
361+
@trigger_error($deprecation[1], E_USER_DEPRECATED);
362+
}
363+
}
364+
}
365+
352366
if (!$process->isSuccessful() || !preg_match('/^O\:\d+\:/', $process->getOutput())) {
353367
throw new \RuntimeException(sprintf('OUTPUT: %s ERROR OUTPUT: %s', $process->getOutput(), $process->getErrorOutput()));
354368
}

src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1832,7 +1832,13 @@ private function export($value)
18321832

18331833
private function doExport($value)
18341834
{
1835-
$export = var_export($value, true);
1835+
if (is_string($value) && false !== strpos($value, "\n")) {
1836+
$cleanParts = explode("\n", $value);
1837+
$cleanParts = array_map(function ($part) { return var_export($part, true); }, $cleanParts);
1838+
$export = implode('."\n".', $cleanParts);
1839+
} else {
1840+
$export = var_export($value, true);
1841+
}
18361842

18371843
if ("'" === $export[0] && $export !== $resolvedExport = $this->container->resolveEnvPlaceholders($export, "'.\$this->getEnv('string:%s').'")) {
18381844
$export = $resolvedExport;

src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ public function testDumpOptimizationString()
7171
'optimize concatenation with empty string' => 'string1%empty_value%string2',
7272
'optimize concatenation from the start' => '%empty_value%start',
7373
'optimize concatenation at the end' => 'end%empty_value%',
74+
'new line' => "string with \nnew line",
7475
));
7576
$definition->setPublic(true);
7677

0 commit comments

Comments
 (0)