From d4da802f09a5479d98eee54f6ac91072111aaf8a Mon Sep 17 00:00:00 2001
From: catch <catch@35733.no-reply.drupal.org>
Date: Tue, 3 Jan 2023 10:27:28 +0000
Subject: [PATCH] Issue #3067580 by alexpott, andypost, pooja saraah, Niklan,
 smustgrave: Deprecate the AJAX RenderElement

---
 core/lib/Drupal/Core/Render/Element/Ajax.php  | 13 +++++
 .../Drupal/Core/Render/ElementInfoManager.php | 11 +++++
 .../src/Element/Deprecated.php                | 29 +++++++++++
 .../Render/Element/DeprecatedElementTest.php  | 48 +++++++++++++++++++
 4 files changed, 101 insertions(+)
 create mode 100644 core/modules/system/tests/modules/element_info_test/src/Element/Deprecated.php
 create mode 100644 core/tests/Drupal/KernelTests/Core/Render/Element/DeprecatedElementTest.php

diff --git a/core/lib/Drupal/Core/Render/Element/Ajax.php b/core/lib/Drupal/Core/Render/Element/Ajax.php
index d22d7edf221a..3edc75f0cca9 100644
--- a/core/lib/Drupal/Core/Render/Element/Ajax.php
+++ b/core/lib/Drupal/Core/Render/Element/Ajax.php
@@ -10,9 +10,22 @@
  * @ingroup ajax
  *
  * @RenderElement("ajax")
+ *
+ * @deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Return an
+ *   \Drupal\Core\Ajax\AjaxResponse instead.
+ *
+ * @see https://www.drupal.org/node/3068104
  */
 class Ajax extends RenderElement {
 
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    @trigger_error('\Drupal\Core\Render\Element\Ajax is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Return an \Drupal\Core\Ajax\AjaxResponse instead. See https://www.drupal.org/node/3068104', E_USER_DEPRECATED);
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/core/lib/Drupal/Core/Render/ElementInfoManager.php b/core/lib/Drupal/Core/Render/ElementInfoManager.php
index ed927652634d..37f485793b58 100644
--- a/core/lib/Drupal/Core/Render/ElementInfoManager.php
+++ b/core/lib/Drupal/Core/Render/ElementInfoManager.php
@@ -107,6 +107,16 @@ protected function buildInfo($theme_name) {
 
     // Otherwise, rebuild and cache.
     $info = [];
+    $previous_error_handler = set_error_handler(function ($severity, $message, $file, $line) use (&$previous_error_handler) {
+      // Ignore deprecations while building element information.
+      if ($severity === E_USER_DEPRECATED) {
+        // Don't execute PHP internal error handler.
+        return TRUE;
+      }
+      if ($previous_error_handler) {
+        return $previous_error_handler($severity, $message, $file, $line);
+      }
+    });
     foreach ($this->getDefinitions() as $element_type => $definition) {
       $element = $this->createInstance($element_type);
       $element_info = $element->getInfo();
@@ -119,6 +129,7 @@ protected function buildInfo($theme_name) {
       }
       $info[$element_type] = $element_info;
     }
+    restore_error_handler();
 
     foreach ($info as $element_type => $element) {
       $info[$element_type]['#type'] = $element_type;
diff --git a/core/modules/system/tests/modules/element_info_test/src/Element/Deprecated.php b/core/modules/system/tests/modules/element_info_test/src/Element/Deprecated.php
new file mode 100644
index 000000000000..8716fe90343e
--- /dev/null
+++ b/core/modules/system/tests/modules/element_info_test/src/Element/Deprecated.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Drupal\element_info_test\Element;
+
+use Drupal\Core\Render\Element\RenderElement;
+
+/**
+ * Provides deprecated render element for testing.
+ *
+ * @RenderElement("deprecated")
+ */
+class Deprecated extends RenderElement {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    @trigger_error(__CLASS__ . ' is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. See https://www.drupal.org/node/3068104', E_USER_DEPRECATED);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo() {
+    return [];
+  }
+
+}
diff --git a/core/tests/Drupal/KernelTests/Core/Render/Element/DeprecatedElementTest.php b/core/tests/Drupal/KernelTests/Core/Render/Element/DeprecatedElementTest.php
new file mode 100644
index 000000000000..916becbd9878
--- /dev/null
+++ b/core/tests/Drupal/KernelTests/Core/Render/Element/DeprecatedElementTest.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace Drupal\KernelTests\Core\Render\Element;
+
+use Drupal\KernelTests\KernelTestBase;
+
+/**
+ * @group Render
+ */
+class DeprecatedElementTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['element_info_test'];
+
+  /**
+   * Tests that render elements can trigger deprecations in their constructor.
+   */
+  public function testBuildInfo() {
+    $info_manager = $this->container->get('plugin.manager.element_info');
+    $this->assertSame([
+      '#type' => 'deprecated',
+      '#defaults_loaded' => TRUE,
+    ], $info_manager->getInfo('deprecated'));
+
+    // Ensure the constructor is triggering a deprecation error.
+    $previous_error_handler = set_error_handler(function ($severity, $message, $file, $line) use (&$previous_error_handler) {
+      // Convert deprecation error into a catchable exception.
+      if ($severity === E_USER_DEPRECATED) {
+        throw new \ErrorException($message, 0, $severity, $file, $line);
+      }
+      if ($previous_error_handler) {
+        return $previous_error_handler($severity, $message, $file, $line);
+      }
+    });
+
+    try {
+      $info_manager->createInstance('deprecated');
+      $this->fail('No deprecation error triggered.');
+    }
+    catch (\ErrorException $e) {
+      $this->assertSame('Drupal\element_info_test\Element\Deprecated is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. See https://www.drupal.org/node/3068104', $e->getMessage());
+    }
+    restore_error_handler();
+  }
+
+}
-- 
GitLab