diff --git a/.travis.yml b/.travis.yml index 353a51e18077d..d6ad39a1a0d06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,6 +29,7 @@ matrix: env: deps=high - php: 7.3 env: deps=low + - php: 7.4snapshot fast_finish: true @@ -163,7 +164,7 @@ before_install: tfold ext.libsodium tpecl libsodium sodium.so $INI fi - tfold ext.apcu tpecl apcu-5.1.16 apcu.so $INI + tfold ext.apcu tpecl apcu-5.1.17 apcu.so $INI tfold ext.mongodb tpecl mongodb-1.6.0alpha1 mongodb.so $INI tfold ext.igbinary tpecl igbinary-2.0.8 igbinary.so $INI tfold ext.zookeeper tpecl zookeeper-0.7.1 zookeeper.so $INI diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php index f9457c937d531..73fbfe44936f6 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php @@ -39,12 +39,14 @@ class DeprecationErrorHandler 'otherCount' => 0, 'remaining directCount' => 0, 'remaining indirectCount' => 0, + 'mutedCount' => 0, 'unsilenced' => [], 'remaining self' => [], 'legacy' => [], 'other' => [], 'remaining direct' => [], 'remaining indirect' => [], + 'muted' => [], ]; private static $isRegistered = false; @@ -128,7 +130,7 @@ public function handleError($type, $msg, $file, $line, $context = []) } $deprecation = new Deprecation($msg, debug_backtrace(), $file); - $group = 'other'; + $group = $deprecation->isMuted() ? 'muted' : 'other'; if ($deprecation->originatesFromAnObject()) { $class = $deprecation->originatingClass(); @@ -139,6 +141,8 @@ public function handleError($type, $msg, $file, $line, $context = []) $group = 'unsilenced'; } elseif ($deprecation->isLegacy(self::$utilPrefix)) { $group = 'legacy'; + } elseif ($deprecation->isMuted()) { + $group = 'muted'; } else { $group = [ Deprecation::TYPE_SELF => 'remaining self', @@ -186,6 +190,9 @@ public function shutdown() } $groups = ['unsilenced', 'remaining self', 'remaining direct', 'remaining indirect', 'legacy', 'other']; + if ($configuration->scream()) { + $groups[] = 'muted'; + } $this->displayDeprecations($groups, $configuration); diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Configuration.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Configuration.php index 6b42814bbc906..fc75b7904aaa0 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Configuration.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Configuration.php @@ -36,14 +36,25 @@ class Configuration */ private $verboseOutput = true; + /** + * @var bool + */ + private $scream = false; + /** * @param int[] $thresholds A hash associating groups to thresholds * @param string $regex Will be matched against messages, to decide * whether to display a stack trace * @param bool $verboseOutput - */ - private function __construct(array $thresholds = [], $regex = '', $verboseOutput = true) - { + * @param bool $scream whether to display muted notices (they + * still will not make the build fail) + */ + private function __construct( + array $thresholds = [], + $regex = '', + $verboseOutput = true, + $scream = false + ) { $groups = ['total', 'indirect', 'direct', 'self']; foreach ($thresholds as $group => $threshold) { @@ -73,6 +84,7 @@ private function __construct(array $thresholds = [], $regex = '', $verboseOutput } $this->regex = $regex; $this->verboseOutput = $verboseOutput; + $this->scream = $scream; } /** @@ -92,7 +104,7 @@ public function tolerates(array $deprecations) { $deprecationCounts = []; foreach ($deprecations as $key => $deprecation) { - if (false !== strpos($key, 'Count') && false === strpos($key, 'legacy')) { + if (false !== strpos($key, 'Count') && false === strpos($key, 'legacy') && false === strpos($key, 'muted')) { $deprecationCounts[$key] = $deprecation; } } @@ -135,6 +147,14 @@ public function verboseOutput() return $this->verboseOutput; } + /** + * @return bool + */ + public function scream() + { + return $this->scream; + } + /** * @param string $serializedConfiguration an encoded string, for instance * max[total]=1234&max[indirect]=42 @@ -145,7 +165,7 @@ public static function fromUrlEncodedString($serializedConfiguration) { parse_str($serializedConfiguration, $normalizedConfiguration); foreach (array_keys($normalizedConfiguration) as $key) { - if (!\in_array($key, ['max', 'disabled', 'verbose'], true)) { + if (!\in_array($key, ['max', 'disabled', 'verbose', 'scream'], true)) { throw new \InvalidArgumentException(sprintf('Unknown configuration option "%s"', $key)); } } @@ -159,10 +179,16 @@ public static function fromUrlEncodedString($serializedConfiguration) $verboseOutput = (bool) $normalizedConfiguration['verbose']; } + $scream = false; + if (isset($normalizedConfiguration['scream'])) { + $scream = (bool) $normalizedConfiguration['scream']; + } + return new self( isset($normalizedConfiguration['max']) ? $normalizedConfiguration['max'] : [], '', - $verboseOutput + $verboseOutput, + $scream ); } diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php index ea84256663528..c392d6113a70a 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php @@ -169,6 +169,20 @@ public function isLegacy($utilPrefix) || \in_array('legacy', $test::getGroups($class, $method), true); } + /** + * @return bool + */ + public function isMuted() + { + if (!isset($this->trace[0]['class'])) { + return false; + } + + return 0 === strpos($this->trace[0]['class'], 'PHPUnit\\') && \in_array($this->message, [ + 'Function ReflectionType::__toString() is deprecated', + ], true); + } + /** * Tells whether both the calling package and the called package are vendor * packages. diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/ConfigurationTest.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/ConfigurationTest.php index 39e792cd3a2cb..fdf021717e607 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/ConfigurationTest.php +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/ConfigurationTest.php @@ -128,6 +128,17 @@ public function testItNoticesExceededIndirectThreshold() ])); } + public function testItIgnoresMutedDeprecations() + { + $configuration = Configuration::fromUrlEncodedString('max[total]=0'); + $this->assertTrue($configuration->tolerates([ + 'mutedCount' => 9000, + 'remaining selfCount' => 0, + 'remaining directCount' => 0, + 'remaining indirectCount' => 0, + ])); + } + public function testIndirectThresholdIsUsedAsADefaultForDirectAndSelfThreshold() { $configuration = Configuration::fromUrlEncodedString('max[indirect]=1'); @@ -181,6 +192,18 @@ public function testItCanBeDisabled() $this->assertFalse($configuration->isEnabled()); } + public function testMutedDeprecationsAreHiddenByDefault() + { + $configuration = Configuration::fromUrlEncodedString(''); + $this->assertFalse($configuration->scream()); + } + + public function testMutedDeprecationsCanBeShownAnyway() + { + $configuration = Configuration::fromUrlEncodedString('scream=true'); + $this->assertTrue($configuration->scream()); + } + public function testItCanBeShushed() { $configuration = Configuration::fromUrlEncodedString('verbose'); diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationTest.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationTest.php index 0c8708bb35f00..e6fc9d7900ee1 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationTest.php +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationTest.php @@ -55,6 +55,66 @@ public function testItRulesOutFilesOutsideVendorsAsIndirect() $this->assertNotSame(Deprecation::TYPE_INDIRECT, $deprecation->getType()); } + /** + * @param bool $muted + * @param string $callingClass + * @param string $message + * + * @dataProvider mutedProvider + */ + public function testItMutesOnlySpecificErrorMessagesWhenTheCallingCodeIsInPhpunit( + $muted, + $callingClass, + $message + ) { + $trace = $this->debugBacktrace(); + array_unshift($trace, ['class' => $callingClass]); + $deprecation = new Deprecation( + $message, + $trace, + __FILE__ + ); + $this->assertSame($muted, $deprecation->isMuted()); + } + + public function mutedProvider() + { + yield 'not from phpunit, and not a whitelisted message' => [ + false, + \My\Source\Code::class, + 'Self deprecating humor is deprecated by itself' + ]; + yield 'from phpunit, but not a whitelisted message' => [ + false, + \PHPUnit\Random\Piece\Of\Code::class, + 'Self deprecating humor is deprecated by itself' + ]; + yield 'whitelisted message, but not from phpunit' => [ + false, + \My\Source\Code::class, + 'Function ReflectionType::__toString() is deprecated', + ]; + yield 'from phpunit and whitelisted message' => [ + true, + \PHPUnit\Random\Piece\Of\Code::class, + 'Function ReflectionType::__toString() is deprecated', + ]; + } + + public function testNotMutedIfNotCalledFromAClass() + { + $deprecation = new Deprecation( + 'Function ReflectionType::__toString() is deprecated', + [ + ['file' => 'my-procedural-controller.php'], + ['file' => 'my-procedural-framework.php'] + ], + __FILE__ + ); + $this->assertFalse($deprecation->isMuted()); + } + + /** * This method is here to simulate the extra level from the piece of code * triggering an error to the error handler