diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php index 62e09c461cca5..615482a6cdebd 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php @@ -52,13 +52,18 @@ class Deprecation */ public function __construct($message, array $trace, $file) { + if (isset($trace[2]['function']) && 'trigger_deprecation' === $trace[2]['function']) { + $file = $trace[2]['file']; + array_splice($trace, 1, 1); + } + $this->trace = $trace; $this->message = $message; - $i = \count($trace); - while (1 < $i && $this->lineShouldBeSkipped($trace[--$i])) { + $i = \count($this->trace); + while (1 < $i && $this->lineShouldBeSkipped($this->trace[--$i])) { // No-op } - $line = $trace[$i]; + $line = $this->trace[$i]; $this->triggeringFile = $file; if (isset($line['object']) || isset($line['class'])) { if (isset($line['class']) && 0 === strpos($line['class'], SymfonyTestsListenerFor::class)) { diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_app/AppService.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_app/AppService.php new file mode 100644 index 0000000000000..1e867c9d1c29f --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_app/AppService.php @@ -0,0 +1,41 @@ +deprecatedApi(); + + $service2 = new SomeOtherService(); + $service2->deprecatedApi(); + } + + public function selfDeprecation(bool $useContracts = false) + { + $args = [__FUNCTION__, __FUNCTION__]; + if ($useContracts) { + trigger_deprecation('App', '3.0', sprintf('%s is deprecated, use %s_new instead.', ...$args)); + } else { + @trigger_error(sprintf('Since App 3.0: %s is deprecated, use %s_new instead.', ...$args), E_USER_DEPRECATED); + } + } + + public function directDeprecation(bool $useContracts = false) + { + $service = new SomeService(); + $service->deprecatedApi($useContracts); + } + + public function indirectDeprecation(bool $useContracts = false) + { + $service = new SomeService(); + $service->indirectDeprecatedApi($useContracts); + } +} + diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/SomeService.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/SomeService.php index 6a354103ff3ce..1e8dce3304140 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/SomeService.php +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/SomeService.php @@ -2,13 +2,22 @@ namespace acme\lib; +use bar\lib\AnotherService; + class SomeService { - public function deprecatedApi() + public function deprecatedApi(bool $useContracts = false) + { + $args = [__FUNCTION__, __FUNCTION__]; + if ($useContracts) { + trigger_deprecation('acme/lib', '3.0', sprintf('%s is deprecated, use %s_new instead.', ...$args)); + } else { + @trigger_error(sprintf('Since acme/lib 3.0: %s is deprecated, use %s_new instead.', ...$args), E_USER_DEPRECATED); + } + } + + public function indirectDeprecatedApi(bool $useContracts = false) { - @trigger_error( - __FUNCTION__.' is deprecated! You should stop relying on it!', - E_USER_DEPRECATED - ); + (new AnotherService())->deprecatedApi($useContracts); } } diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/bar/lib/AnotherService.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/bar/lib/AnotherService.php new file mode 100644 index 0000000000000..ae072a027a55a --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/bar/lib/AnotherService.php @@ -0,0 +1,16 @@ + [__DIR__.'/../../fake_app/'], + 'acme\\lib\\' => [__DIR__.'/../acme/lib/'], + 'bar\\lib\\' => [__DIR__.'/../bar/lib/'], + ]; + } + + public function loadClass($className) + { + foreach ($this->getPrefixesPsr4() as $prefix => $baseDirs) { + if (strpos($className, $prefix) !== 0) { + continue; + } + + foreach ($baseDirs as $baseDir) { + $file = str_replace([$prefix, '\\'], [$baseDir, '/'], $className.'.php'); + if (file_exists($file)) { + require $file; + } + } + } } } class ComposerAutoloaderInitFake { + private static $loader; + public static function getLoader() { - return new ComposerLoaderFake(); + if (null === self::$loader) { + self::$loader = new ComposerLoaderFake(); + spl_autoload_register([self::$loader, 'loadClass']); + } + + return self::$loader; } } diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/lagging_vendor.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/lagging_vendor.phpt index 2f7686fd98819..b5253df4299e4 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/lagging_vendor.phpt +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/lagging_vendor.phpt @@ -35,5 +35,5 @@ require __DIR__.'/fake_vendor/acme/outdated-lib/outdated_file.php'; --EXPECTF-- Remaining indirect deprecation notices (1) - 1x: deprecatedApi is deprecated! You should stop relying on it! + 1x: Since acme/lib 3.0: deprecatedApi is deprecated, use deprecatedApi_new instead. 1x in SomeService::deprecatedApi from acme\lib diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/partially_quiet.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/partially_quiet.phpt index d45c6f9af2687..f1fb64ed137ba 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/partially_quiet.phpt +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/partially_quiet.phpt @@ -28,7 +28,7 @@ Remaining direct deprecation notices (1) Remaining indirect deprecation notices (1) - 1x: deprecatedApi is deprecated! You should stop relying on it! + 1x: Since acme/lib 3.0: deprecatedApi is deprecated, use deprecatedApi_new instead. 1x in SomeService::deprecatedApi from acme\lib Legacy deprecation notices (2) diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/quiet_but_failing.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/quiet_but_failing.phpt index 9c73d3c4430ae..8d8f8b4ff490d 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/quiet_but_failing.phpt +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/quiet_but_failing.phpt @@ -35,5 +35,5 @@ require __DIR__.'/fake_vendor/acme/outdated-lib/outdated_file.php'; --EXPECTF-- Remaining indirect deprecation notices (1) - 1x: deprecatedApi is deprecated! You should stop relying on it! + 1x: Since acme/lib 3.0: deprecatedApi is deprecated, use deprecatedApi_new instead. 1x in SomeService::deprecatedApi from acme\lib diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/trigger_deprecation_types.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/trigger_deprecation_types.phpt new file mode 100644 index 0000000000000..261b6ec83f675 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/trigger_deprecation_types.phpt @@ -0,0 +1,58 @@ +--TEST-- +Test deprecation types with trigger_deprecation +--FILE-- +selfDeprecation(true); +(new \App\Services\AppService())->directDeprecation(true); +(new \App\Services\AppService())->indirectDeprecation(true); +trigger_deprecation('foo/bar', '2.0', 'func is deprecated, use new instead.'); +?> +--EXPECTF-- +Remaining self deprecation notices (1) + + 1x: Since App 3.0: selfDeprecation is deprecated, use selfDeprecation_new instead. + 1x in AppService::selfDeprecation from App\Services + +Remaining direct deprecation notices (1) + + 1x: Since acme/lib 3.0: deprecatedApi is deprecated, use deprecatedApi_new instead. + 1x in AppService::directDeprecation from App\Services + +Remaining indirect deprecation notices (1) + + 1x: Since bar/lib 3.0: deprecatedApi is deprecated, use deprecatedApi_new instead. + 1x in AppService::indirectDeprecation from App\Services + +Other deprecation notices (1) + + 1x: Since foo/bar 2.0: func is deprecated, use new instead. diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/trigger_error_types.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/trigger_error_types.phpt new file mode 100644 index 0000000000000..4dd6bdaed9f0b --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/trigger_error_types.phpt @@ -0,0 +1,58 @@ +--TEST-- +Test deprecation types with trigger_error +--FILE-- +selfDeprecation(); +(new \App\Services\AppService())->directDeprecation(); +(new \App\Services\AppService())->indirectDeprecation(); +@trigger_error('Since foo/bar 2.0: func is deprecated, use new instead.', E_USER_DEPRECATED); +?> +--EXPECTF-- +Remaining self deprecation notices (1) + + 1x: Since App 3.0: selfDeprecation is deprecated, use selfDeprecation_new instead. + 1x in AppService::selfDeprecation from App\Services + +Remaining direct deprecation notices (1) + + 1x: Since acme/lib 3.0: deprecatedApi is deprecated, use deprecatedApi_new instead. + 1x in AppService::directDeprecation from App\Services + +Remaining indirect deprecation notices (1) + + 1x: Since bar/lib 3.0: deprecatedApi is deprecated, use deprecatedApi_new instead. + 1x in AppService::indirectDeprecation from App\Services + +Other deprecation notices (1) + + 1x: Since foo/bar 2.0: func is deprecated, use new instead. diff --git a/src/Symfony/Bridge/PhpUnit/composer.json b/src/Symfony/Bridge/PhpUnit/composer.json index a285a435ce9bd..60c13a0aabc70 100644 --- a/src/Symfony/Bridge/PhpUnit/composer.json +++ b/src/Symfony/Bridge/PhpUnit/composer.json @@ -45,5 +45,8 @@ "name": "phpunit/phpunit", "url": "https://github.com/sebastianbergmann/phpunit" } + }, + "require-dev": { + "symfony/deprecation-contracts": "^2.1" } }