8000 WIP: [PhpunitBridge][HttpKernel] Read compiler deprecations from file… · symfony/symfony@d2749e4 · GitHub
[go: up one dir, main page]

Skip to content

Commit d2749e4

Browse files
committed
WIP: [PhpunitBridge][HttpKernel] Read compiler deprecations from file when the container is already built
1 parent f2590d1 commit d2749e4

File tree

5 files changed

+143
-22
lines changed

5 files changed

+143
-22
lines changed

src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,16 @@ public static function register($mode = 0)
6565
return $memoizedMode = $mode;
6666
};
6767

68+
$isFromTestListeners = function(array $trace) : bool {
69+
return 0 === strpos($trace['class'], 'Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerFor');
70+
};
71+
72+
$isFromContainerDeprecations = function (array $trace) : bool {
73+
return $trace['class'] == 'Symfony\Bundle\FrameworkBundle\Test\KernelTestCase'
74+
&& isset($trace['function'])
75+
&& $trace['function'] == 'tearDownAfterClass';
76+
};
77+
6878
$inVendors = function ($path) {
6979
/** @var string[] absolute paths to vendor directories */
7080
static $vendors;
@@ -104,7 +114,8 @@ public static function register($mode = 0)
104114
'other' => array(),
105115
'remaining vendor' => array(),
106116
);
107-
$deprecationHandler = function ($type, $msg, $file, $line, $context = array()) use (&$deprecations, $getMode, $UtilPrefix, $inVendors) {
117+
$containerDeprecations = [];
118+
$deprecationHandler = function ($type, $msg, $file, $line, $context = array()) use (&$deprecations, &$containerDeprecations, $getMode, $UtilPrefix, $inVendors, $isFromTestListeners, $isFromContainerDeprecations) {
108119
$mode = $getMode();
109120
if ((E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) || DeprecationErrorHandler::MODE_DISABLED === $mode) {
110121
$ErrorHandler = $UtilPrefix.'ErrorHandler';
@@ -121,9 +132,11 @@ public static function register($mode = 0)
121132
// No-op
122133
}
123134

135+
$containerDeprecation = false;
136+
$skip = false;
124137
if (isset($trace[$i]['object']) || isset($trace[$i]['class'])) {
125-
if (isset($trace[$i]['class']) && 0 === strpos($trace[$i]['class'], 'Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerFor')) {
126-
$parsedMsg = unserialize($msg);
138+
if (isset($trace[$i]['class']) && ($isFromTestListeners($trace[$i]) || ($ifcd = $isFromContainerDeprecations($trace[$i]))) && $parsedMsg = @unserialize($msg)) {
139+
$containerDeprecation = isset($ifcd) && $ifcd && $parsedMsg;
127140
$msg = $parsedMsg['deprecation'];
128141
$class = $parsedMsg['class'];
129142
$method = $parsedMsg['method'];
@@ -169,16 +182,32 @@ public static function register($mode = 0)
169182
exit(1);
170183
}
171184
if ('legacy' !== $group && DeprecationErrorHandler::MODE_WEAK !== $mode) {
172-
$ref = &$deprecations[$group][$msg]['count'];
173-
++$ref;
174-
$ref = &$deprecations[$group][$msg][$class.'::'.$method];
175-
++$ref;
185+
// Some container deprecations may be send at container build time and at application runtime
186+
// The deprecation message must be displayed only once
187+
if ($containerDeprecation) {
188+
if (isset($deprecations[$group][$msg][$class.'::'.$method])) {
189+
$skip = true;
190+
}
191+
$containerDeprecations[implode('|', [$msg, $class, $method])] = true;
192+
} elseif (isset($containerDeprecations[implode('|', [$msg, $class, $method])])) {
193+
$skip = true;
194+
}
195+
196+
if (!$skip) {
197+
$ref = &$deprecations[$group][$msg]['count'];
198+
++$ref;
199+
$ref = &$deprecations[$group][$msg][$class.'::'.$method];
200+
++$ref;
201+
}
176202
}
177203
} elseif (DeprecationErrorHandler::MODE_WEAK !== $mode) {
178204
$ref = &$deprecations[$group][$msg]['count'];
179205
++$ref;
180206
}
181-
++$deprecations[$group.'Count'];
207+
208+
if (!$skip) {
209+
++$deprecations[$group.'Count'];
210+
}
182211
};
183212
$oldErrorHandler = set_error_handler($deprecationHandler);
184213

@@ -321,7 +350,7 @@ private static function hasColorSupport()
321350

322351
if (\DIRECTORY_SEPARATOR === '\\') {
323352
return (\function_exists('sapi_windows_vt100_support')
324-
&& sapi_windows_vt100_support(STDOUT))
353+
&& sapi_windows_vt100_support(STDOUT))
325354
|| false !== getenv('ANSICON')
326355
|| 'ON' === getenv('ConEmuANSI')
327356
|| 'xterm' === getenv('TERM');

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,12 @@ public function endTest($test, $time)
272272
unlink($this->runsInSeparateProcess);
273273
putenv('SYMFONY_DEPRECATIONS_SERIALIZE');
274274
foreach ($deprecations ? unserialize($deprecations) : array() as $deprecation) {
275-
$error = serialize(array('deprecation' => $deprecation[1], 'class' => $className, 'method' => $test->getName(false), 'triggering_file' => isset($deprecation[2]) ? $deprecation[2] : null));
275+
if (@unserialize($deprecation[1])) { // Compiler deprecations are already serialized
276+
$error = $deprecation[1];
277+
} else {
278+
$error = serialize(array('deprecation' => $deprecation[1], 'class' => $className, 'method' => $test->getName(false), 'triggering_file' => isset($deprecation[2]) ? $deprecation[2] : null));
279+
}
280+
276281
if ($deprecation[0]) {
277282
@trigger_error($error, E_USER_DEPRECATED);
278283
} else {

src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ abstract class KernelTestCase extends TestCase
3535
*/
3636
protected static $container;
3737

38+
private static $compilerDeprecationsTriggered = false;
39+
3840
/**
3941
* @return string The Kernel class name
4042
*
@@ -133,4 +135,20 @@ protected function tearDown()
133135
{
134136
static::ensureKernelShutdown();
135137
}
138+
139+
public static function tearDownAfterClass()
140+
{
141+
if (!self::$compilerDeprecationsTriggered) {
142+
$compilerDeprecated = getenv('SYMFONY_COMPILER_DEPRECATIONS');
143+
if ($compilerDeprecated && file_exists($compilerDeprecated)) {
144+
foreach (unserialize(file_get_contents($compilerDeprecated)) as $log) {
145+
@trigger_error(serialize($log), E_USER_DEPRECATED);
146+
}
147+
148+
self::$compilerDeprecationsTriggered = true;
149+
}
150+
}
151+
152+
parent::tearDownAfterClass();
153+
}
136154
}

src/Symfony/Component/HttpKernel/Kernel.php

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,7 @@ protected function initializeContainer()
476476
$cacheDir = $this->warmupDir ?: $this->getCacheDir();
477477
$cache = new ConfigCache($cacheDir.'/'.$class.'.php', $this->debug);
478478
$oldContainer = null;
479+
$deprecationsFilename = $cacheDir.'/'.$class.'Deprecations.log';
479480
if ($fresh = $cache->isFresh()) {
480481
// Silence E_WARNING to ignore "include" failures - don't use "@" to prevent silencing fatal errors
481482
$errorLevel = error_reporting(\E_ALL ^ \E_WARNING);
@@ -494,13 +495,16 @@ protected function initializeContainer()
494495
}
495496

496497
if ($fresh) {
498+
if ($this->debug) {
499+
putenv("SYMFONY_COMPILER_DEPRECATIONS=$deprecationsFilename");
500+
}
501+
497502
return;
498503
}
499504

500505
if ($this->debug) {
501506
$collectedLogs = array();
502-
$previousHandler = \defined('PHPUNIT_COMPOSER_INSTALL');
503-
$previousHandler = $previousHandler ?: set_error_handler(function ($type, $message, $file, $line) use (&$collectedLogs, &$previousHandler) {
507+
$previousHandler = set_error_handler(function ($type, $message, $file, $line) use (&$collectedLogs, &$previousHandler) {
504508
if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) {
505509
return $previousHandler ? $previousHandler($type, $message, $file, $line) : false;
506510
}
@@ -511,14 +515,27 @@ protected function initializeContainer()
511515
return;
512516
}
513517

514-
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5);
515-
// Clean the trace by removing first frames added by the error handler itself.
516-
for ($i = 0; isset($backtrace[$i]); ++$i) {
517-
if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) {
518-
$backtrace = \array_slice($backtrace, 1 + $i);
519-
break;
518+
if (\defined('PHPUNIT_COMPOSER_INSTALL')) {
519+
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
520+
$i = \count($backtrace);
521+
while (1 < $i && (!isset($backtrace[--$i]['class']) || ('ReflectionMethod' === $backtrace[$i]['class'] || 0 === strpos($backtrace[$i]['class'], 'PHPUnit_') || 0 === strpos($backtrace[$i]['class'], 'PHPUnit\\')))) {
522+
// No-op
523+
}
524+
$backtrace = \array_slice($backtrace, $i);
525+
} else {
526+
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5);
527+
// Clean the trace by removing first frames added by the error handler itself.
528+
for ($i = 0; isset($backtrace[$i]); ++$i) {
529+
if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) {
530+
$backtrace = \array_slice($backtrace, 1 + $i);
531+
break;
532+
}
520533
}
521534
}
535+
536+
$class = $backtrace[0]['class'];
537+
$function = $backtrace[0]['function'];
538+
522539
// Remove frames added by DebugClassLoader.
523540
for ($i = \count($backtrace) - 2; 0 < $i; --$i) {
524541
if (DebugClassLoader::class === ($backtrace[$i]['class'] ?? null)) {
@@ -530,8 +547,12 @@ protected function initializeContainer()
530547
$collectedLogs[$message] = array(
531548
'type' => $type,
532549
'message' => $message,
550+
'deprecation' => $message,
533551
'file' => $file,
552+
'triggering_file' => $file,
534553
'line' => $line,
554+
'class' => $class,
555+
'method' => $function,
535556
'trace' => array($backtrace[0]),
536557
'count' => 1,
537558
);
@@ -543,10 +564,10 @@ protected function initializeContainer()
543564
$container = $this->buildContainer();
544565
$container->compile();
545566
} finally {
546-
if ($this->debug && true !== $previousHandler) {
567+
if ($this->debug) {
547568
restore_error_handler();
548569

549-
file_put_contents($cacheDir.'/'.$class.'Deprecations.log', serialize(array_values($collectedLogs)));
570+
file_put_contents($deprecationsFilename, serialize(array_values($collectedLogs)));
550571
file_put_contents($cacheDir.'/'.$class.'Compiler.log', null !== $container ? implode("\n", $container->getCompiler()->getLog()) : '');
551572
}
552573
}

src/Symfony/Component/HttpKernel/Tests/KernelTest.php

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,53 @@ public function testBootKernelSeveralTimesOnlyInitializesBundlesOnce()
142142
$kernel->boot();
143143
}
144144

145+
public function providerSfCompilerDeprecationsEnvVarOnlySetIfContainerAlreadyBuilt()
146+
{
147+
return array(
148+
array(true, true, true, true),
149+
array(true, false, false, false),
150+
array(false, true, true, false),
151+
array(false, false, true, false),
152+
);
153+
}
154+
155+
/**
156+
* @dataProvider providerSfCompilerDeprecationsEnvVarOnlySetIfContainerAlreadyBuilt
157+
*/
158+
public function testSfCompilerDeprecationsEnvVarOnlySetIfContainerAlreadyBuilt($isDebugMode, $cacheIsFresh, $resetEnvVar, $expectedSymfonyCompilerDeprecationsSet)
159+
{
160+
if ($resetEnvVar) {
161+
putenv('SYMFONY_COMPILER_DEPRECATIONS');
162+
}
163+
164+
$kernel = $this->getKernel(array('getCacheDir', 'isPhpunitComposerInstallDefined'), array(), $isDebugMode);
165+
166+
$fs = new Filesystem();
167+
$cacheDir = __DIR__.'/Fixtures/cache/mycachedir';
168+
$fs->mkdir($cacheDir);
169+
$containerFile = sprintf('%s/MockObject%sTestDebugContainer.php', $cacheDir, get_class($kernel));
170+
$fs->dumpFile($containerFile, '<?php return new Symfony\Component\DependencyInjection\ContainerBuilder();');
171+
if ($cacheIsFresh) {
172+
$fs->dumpFile($containerFile.'.meta', serialize(array()));
173+
}
174+
$kernel
175+
->method('getCacheDir')
176+
->willReturn($cacheDir);
177+
$kernel
178+
->method('isPhpunitComposerInstallDefined')
179+
->willReturn(false);
180+
$kernel->boot();
181+
if ($expectedSymfonyCompilerDeprecationsSet) {
182+
$this->assertEquals(
183+
sprintf('%s/MockObject%sTestDebugContainerDeprecations.log', $cacheDir, get_class($kernel)),
184+
getenv('SYMFONY_COMPILER_DEPRECATIONS')
185+
);
186+
} else {
187+
$this->assertFalse(getenv('SYMFONY_COMPILER_DEPRECATIONS'));
188+
}
189+
$fs->remove($cacheDir);
190+
}
191+
145192
public function testShutdownCallsShutdownOnAllBundles()
146193
{
147194
$bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\Bundle')->getMock();
@@ -650,17 +697,18 @@ protected function getBundle($dir = null, $parent = null, $className = null, $bu
650697
*
651698
* @param array $methods Additional methods to mock (besides the abstract ones)
652699
* @param array $bundles Bundles to register
700+
* @param bool $debug Whether debug mode is activated
653701
*
654702
* @return Kernel
655703
*/
656-
protected function getKernel(array $methods = array(), array $bundles = array())
704+
protected function getKernel(array $methods = array(), array $bundles = array(), $debug = false)
657705
{
658706
$methods[] = 'registerBundles';
659707

660708
$kernel = $this
661709
->getMockBuilder('Symfony\Component\HttpKernel\Kernel')
662710
->setMethods($methods)
663-
->setConstructorArgs(array('test', false))
711+
->setConstructorArgs(array('test', $debug))
664712
->getMockForAbstractClass()
665713
;
666714
$kernel->expects($this->any())

0 commit comments

Comments
 (0)
0