DeprecationListenerTrait.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. <?php
  2. namespace Drupal\Tests\Listeners;
  3. use Drupal\Tests\Traits\ExpectDeprecationTrait;
  4. use PHPUnit\Framework\TestCase;
  5. use PHPUnit\Util\Test;
  6. /**
  7. * Removes deprecations that we are yet to fix.
  8. *
  9. * @internal
  10. * This class will be removed once all the deprecation notices have been
  11. * fixed.
  12. */
  13. trait DeprecationListenerTrait {
  14. use ExpectDeprecationTrait;
  15. /**
  16. * The previous error handler.
  17. *
  18. * @var callable
  19. */
  20. private $previousHandler;
  21. protected function deprecationStartTest($test) {
  22. if ($test instanceof TestCase) {
  23. if ('disabled' !== getenv('SYMFONY_DEPRECATIONS_HELPER')) {
  24. $this->registerErrorHandler($test);
  25. }
  26. if ($this->willBeIsolated($test)) {
  27. putenv('DRUPAL_EXPECTED_DEPRECATIONS_SERIALIZE=' . tempnam(sys_get_temp_dir(), 'exdep'));
  28. }
  29. }
  30. }
  31. /**
  32. * Reacts to the end of a test.
  33. *
  34. * @param \PHPUnit\Framework\Test $test
  35. * The test object that has ended its test run.
  36. * @param float $time
  37. * The time the test took.
  38. */
  39. protected function deprecationEndTest($test, $time) {
  40. /** @var \PHPUnit\Framework\Test $test */
  41. if ($file = getenv('DRUPAL_EXPECTED_DEPRECATIONS_SERIALIZE')) {
  42. putenv('DRUPAL_EXPECTED_DEPRECATIONS_SERIALIZE');
  43. $expected_deprecations = file_get_contents($file);
  44. if ($expected_deprecations) {
  45. $test->expectedDeprecations(unserialize($expected_deprecations));
  46. }
  47. }
  48. if ($file = getenv('SYMFONY_DEPRECATIONS_SERIALIZE')) {
  49. $method = $test->getName(FALSE);
  50. if (strpos($method, 'testLegacy') === 0
  51. || strpos($method, 'provideLegacy') === 0
  52. || strpos($method, 'getLegacy') === 0
  53. || strpos(get_class($test), '\Legacy')
  54. || in_array('legacy', Test::getGroups(get_class($test), $method), TRUE)) {
  55. // This is a legacy test don't skip deprecations.
  56. return;
  57. }
  58. // Need to edit the file of deprecations to remove any skipped
  59. // deprecations.
  60. $deprecations = file_get_contents($file);
  61. $deprecations = $deprecations ? unserialize($deprecations) : [];
  62. $resave = FALSE;
  63. foreach ($deprecations as $key => $deprecation) {
  64. if (in_array($deprecation[1], static::getSkippedDeprecations())) {
  65. unset($deprecations[$key]);
  66. $resave = TRUE;
  67. }
  68. }
  69. if ($resave) {
  70. file_put_contents($file, serialize($deprecations));
  71. }
  72. }
  73. }
  74. /**
  75. * Determines if a test is isolated.
  76. *
  77. * @param \PHPUnit\Framework\TestCase $test
  78. * The test to check.
  79. *
  80. * @return bool
  81. * TRUE if the isolated, FALSE if not.
  82. */
  83. private function willBeIsolated($test) {
  84. if ($test->isInIsolation()) {
  85. return FALSE;
  86. }
  87. $r = new \ReflectionProperty($test, 'runTestInSeparateProcess');
  88. $r->setAccessible(TRUE);
  89. return $r->getValue($test);
  90. }
  91. /**
  92. * A list of deprecations to ignore whilst fixes are put in place.
  93. *
  94. * Do not add any new deprecations to this list. All deprecation errors will
  95. * eventually be removed from this list.
  96. *
  97. * @return string[]
  98. * A list of deprecations to ignore.
  99. *
  100. * @internal
  101. *
  102. * @todo Fix all these deprecations and remove them from this list.
  103. * https://www.drupal.org/project/drupal/issues/2959269
  104. *
  105. * @see https://www.drupal.org/node/2811561
  106. */
  107. public static function getSkippedDeprecations() {
  108. return [
  109. 'MigrateCckField is deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.x. Use \Drupal\migrate_drupal\Annotation\MigrateField instead.',
  110. 'MigrateCckFieldPluginManager is deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.x. Use \Drupal\migrate_drupal\Annotation\MigrateFieldPluginManager instead.',
  111. 'MigrateCckFieldPluginManagerInterface is deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.x. Use \Drupal\migrate_drupal\Annotation\MigrateFieldPluginManagerInterface instead.',
  112. 'The "plugin.manager.migrate.cckfield" service is deprecated. You should use the \'plugin.manager.migrate.field\' service instead. See https://www.drupal.org/node/2751897',
  113. 'The Symfony\Component\ClassLoader\ApcClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use `composer install --apcu-autoloader` instead.',
  114. // The following deprecation is not triggered by DrupalCI testing since it
  115. // is a Windows only deprecation. Remove when core no longer uses
  116. // WinCacheClassLoader in \Drupal\Core\DrupalKernel::initializeSettings().
  117. 'The Symfony\Component\ClassLoader\WinCacheClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use `composer install --apcu-autoloader` instead.',
  118. // The following deprecation message is skipped for testing purposes.
  119. '\Drupal\Tests\SkippedDeprecationTest deprecation',
  120. // These deprecations are triggered by symfony/psr-http-message-factory
  121. // 1.2, which can be installed if you update dependencies on php 7 or
  122. // higher
  123. 'The "Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory" class is deprecated since symfony/psr-http-message-bridge 1.2, use PsrHttpFactory instead.',
  124. 'The "psr7.http_message_factory" service relies on the deprecated "Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory" class. It should either be deprecated or its implementation upgraded.',
  125. // This deprecation comes from behat/mink-browserkit-driver when updating
  126. // symfony/browser-kit to 4.3+.
  127. 'The "Symfony\Component\BrowserKit\Response::getStatus()" method is deprecated since Symfony 4.3, use getStatusCode() instead.',
  128. 'The "core/jquery.ui.checkboxradio" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/node/3067969',
  129. 'The "core/jquery.ui.controlgroup" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/node/3067969',
  130. 'The "core/html5shiv" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/node/3086383',
  131. 'The "core/matchmedia" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/node/3086653',
  132. 'The "core/matchmedia.addListener" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/node/3086653',
  133. 'The "core/classList" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the the native browser implementation instead. See https://www.drupal.org/node/3089511',
  134. 'The "core/jquery.ui.datepicker" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/node/3081864',
  135. 'The "locale/drupal.locale.datepicker" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/node/3081864',
  136. 'The "https://www.drupal.org/link-relations/create" string as a RestResource plugin annotation URI path key is deprecated in Drupal 8.4.0, now a valid link relation type name must be specified, so "create" must be specified instead before Drupal 9.0.0. See https://www.drupal.org/node/2737401.',
  137. ];
  138. }
  139. /**
  140. * Registers an error handler that wraps Symfony's DeprecationErrorHandler.
  141. *
  142. * @see \Symfony\Bridge\PhpUnit\DeprecationErrorHandler
  143. * @see \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait
  144. */
  145. protected function registerErrorHandler($test) {
  146. $deprecation_handler = function ($type, $msg, $file, $line, $context = []) {
  147. // Skip listed deprecations.
  148. if ($type === E_USER_DEPRECATED && in_array($msg, self::getSkippedDeprecations(), TRUE)) {
  149. return;
  150. }
  151. return call_user_func($this->previousHandler, $type, $msg, $file, $line, $context);
  152. };
  153. if ($this->previousHandler) {
  154. set_error_handler($deprecation_handler);
  155. return;
  156. }
  157. $this->previousHandler = set_error_handler($deprecation_handler);
  158. // Register another listener so that we can remove the error handler before
  159. // Symfony's DeprecationErrorHandler checks that it is the currently
  160. // registered handler. Note this is done like this to ensure the error
  161. // handler is removed after SymfonyTestsListenerTrait::endTest() is called.
  162. // SymfonyTestsListenerTrait has its own error handler that needs to be
  163. // removed before this one.
  164. $test_result_object = $test->getTestResultObject();
  165. // It's possible that a test does not have a result object. This can happen
  166. // when a test class does not have any test methods.
  167. if ($test_result_object) {
  168. $reflection_class = new \ReflectionClass($test_result_object);
  169. $reflection_property = $reflection_class->getProperty('listeners');
  170. $reflection_property->setAccessible(TRUE);
  171. $listeners = $reflection_property->getValue($test_result_object);
  172. $listeners[] = new AfterSymfonyListener();
  173. $reflection_property->setValue($test_result_object, $listeners);
  174. }
  175. }
  176. }