diff --git a/src/Hook/XmlrpcHooks.php b/src/Hook/XmlrpcHooks.php
new file mode 100644
index 0000000..58fa07e
--- /dev/null
+++ b/src/Hook/XmlrpcHooks.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Drupal\xmlrpc\Hook;
+
+use Drupal\Core\Url;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Hook implementations for xmlrpc.
+ */
+class XmlrpcHooks {
+  use StringTranslationTrait;
+
+  /**
+   * Implements hook_help().
+   */
+  #[Hook('help')]
+  public function help($route_name) {
+    switch ($route_name) {
+      case 'help.page.xmlrpc':
+        $output = '';
+        $output .= '<h3>' . $this->t('About') . '</h3>';
+        $output .= '<p>' . $this->t('The XML-RPC module gives external systems the opportunity to communicate with the site through the <a href=":url_definition">XML-RPC protocol</a>. An XML-RPC client can communicate with the site by making a request to <a href=":url_endpoint">xmlrpc</a>. For more information, see <a href=":url_docs">the online documentation for the XML-RPC module</a>.', [
+          ':url_definition' => 'https://en.wikipedia.org/wiki/XML-RPC',
+          ':url_endpoint' => Url::fromRoute('xmlrpc')->toString(),
+          ':url_docs' => 'https://drupal.org/documentation/modules/xmlrpc',
+        ]) . '</p>';
+        return $output;
+    }
+  }
+
+}
diff --git a/tests/modules/xmlrpc_test/src/Hook/XmlrpcTestHooks.php b/tests/modules/xmlrpc_test/src/Hook/XmlrpcTestHooks.php
new file mode 100644
index 0000000..5f0ae6a
--- /dev/null
+++ b/tests/modules/xmlrpc_test/src/Hook/XmlrpcTestHooks.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Drupal\xmlrpc_test\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for xmlrpc_test.
+ */
+class XmlrpcTestHooks {
+
+  /**
+   * Implements hook_xmlrpc().
+   */
+  #[Hook('xmlrpc')]
+  public static function xmlrpc() {
+    return [
+      'validator1.arrayOfStructsTest' => 'xmlrpc_test_array_of_structs_test',
+      'validator1.countTheEntities' => 'xmlrpc_test_count_the_entities',
+      'validator1.easyStructTest' => 'xmlrpc_test_easy_struct_test',
+      'validator1.echoStructTest' => 'xmlrpc_test_echo_struct_test',
+      'validator1.manyTypesTest' => 'xmlrpc_test_many_types_test',
+      'validator1.moderateSizeArrayCheck' => 'xmlrpc_test_moderate_size_array_check',
+      'validator1.nestedStructTest' => 'xmlrpc_test_nested_struct_test',
+      'validator1.simpleStructReturnTest' => 'xmlrpc_test_simple_struct_return_test',
+      'messages.messageSizedInKB' => 'xmlrpc_test_message_sized_in_kb',
+      'test.headerEcho' => 'xmlrpc_test_header_echo',
+      'test.noSignatureSimple' => 'xmlrpc_test_header_echo',
+          [
+            'test.noSignatureComplex',
+            'xmlrpc_test_header_echo',
+          ],
+      'test.classCallback' => [
+        'XmlRpcTestClass',
+        'foo',
+      ],
+    ];
+  }
+
+  /**
+   * Implements hook_xmlrpc_alter().
+   *
+   * Hide (or not) the system.methodSignature() service depending on a variable.
+   */
+  #[Hook('xmlrpc_alter')]
+  public static function xmlrpcAlter(&$services) {
+    $xmlrpc_alter = \Drupal::state()->get('xmlrpc_test.alter') || FALSE;
+    if ($xmlrpc_alter) {
+      $remove = NULL;
+      foreach ($services as $key => $value) {
+        if (!is_array($value)) {
+          continue;
+        }
+        if ($value[0] == 'system.methodSignature') {
+          $remove = $key;
+          break;
+        }
+      }
+      if (isset($remove)) {
+        unset($services[$remove]);
+      }
+    }
+  }
+
+}
diff --git a/tests/modules/xmlrpc_test/xmlrpc_test.info.yml b/tests/modules/xmlrpc_test/xmlrpc_test.info.yml
index 7db5f6b..557ea0c 100644
--- a/tests/modules/xmlrpc_test/xmlrpc_test.info.yml
+++ b/tests/modules/xmlrpc_test/xmlrpc_test.info.yml
@@ -1,4 +1,4 @@
-core_version_requirement: "^10 || ^11"
+core_version_requirement: ^10.1 || ^11 || ^12
 description: 'Support module for XML-RPC tests according to the validator1 specification.'
 name: 'XML-RPC Test'
 package: Testing
diff --git a/tests/modules/xmlrpc_test/xmlrpc_test.module b/tests/modules/xmlrpc_test/xmlrpc_test.module
index 5ade45a..c126f51 100644
--- a/tests/modules/xmlrpc_test/xmlrpc_test.module
+++ b/tests/modules/xmlrpc_test/xmlrpc_test.module
@@ -1,5 +1,12 @@
 <?php
 
+/**
+ * @file
+ */
+
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\xmlrpc_test\Hook\XmlrpcTestHooks;
+
 /**
  * @file
  * Test module to support XmlRpcMessagesTest and XmlRpcValidatorTest.
@@ -170,22 +177,9 @@ class XmlRpcTestClass {
 /**
  * Implements hook_xmlrpc().
  */
+#[LegacyHook]
 function xmlrpc_test_xmlrpc() {
-  return [
-    'validator1.arrayOfStructsTest' => 'xmlrpc_test_array_of_structs_test',
-    'validator1.countTheEntities' => 'xmlrpc_test_count_the_entities',
-    'validator1.easyStructTest' => 'xmlrpc_test_easy_struct_test',
-    'validator1.echoStructTest' => 'xmlrpc_test_echo_struct_test',
-    'validator1.manyTypesTest' => 'xmlrpc_test_many_types_test',
-    'validator1.moderateSizeArrayCheck' => 'xmlrpc_test_moderate_size_array_check',
-    'validator1.nestedStructTest' => 'xmlrpc_test_nested_struct_test',
-    'validator1.simpleStructReturnTest' => 'xmlrpc_test_simple_struct_return_test',
-    'messages.messageSizedInKB' => 'xmlrpc_test_message_sized_in_kb',
-    'test.headerEcho' => 'xmlrpc_test_header_echo',
-    'test.noSignatureSimple' => 'xmlrpc_test_header_echo',
-    ['test.noSignatureComplex', 'xmlrpc_test_header_echo'],
-    'test.classCallback' => ['XmlRpcTestClass', 'foo'],
-  ];
+  return \Drupal::service(XmlrpcTestHooks::class)->xmlrpc();
 }
 
 /**
@@ -193,23 +187,9 @@ function xmlrpc_test_xmlrpc() {
  *
  * Hide (or not) the system.methodSignature() service depending on a variable.
  */
+#[LegacyHook]
 function xmlrpc_test_xmlrpc_alter(&$services) {
-  $xmlrpc_alter = \Drupal::state()->get('xmlrpc_test.alter') || FALSE;
-  if ($xmlrpc_alter) {
-    $remove = NULL;
-    foreach ($services as $key => $value) {
-      if (!is_array($value)) {
-        continue;
-      }
-      if ($value[0] == 'system.methodSignature') {
-        $remove = $key;
-        break;
-      }
-    }
-    if (isset($remove)) {
-      unset($services[$remove]);
-    }
-  }
+  \Drupal::service(XmlrpcTestHooks::class)->xmlrpcAlter($services);
 }
 
 /**
diff --git a/tests/modules/xmlrpc_test/xmlrpc_test.services.yml b/tests/modules/xmlrpc_test/xmlrpc_test.services.yml
new file mode 100644
index 0000000..62afccf
--- /dev/null
+++ b/tests/modules/xmlrpc_test/xmlrpc_test.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\xmlrpc_test\Hook\XmlrpcTestHooks:
+    class: Drupal\xmlrpc_test\Hook\XmlrpcTestHooks
+    autowire: true
diff --git a/tests/src/Functional/XmlRpcBasicTest.php b/tests/src/Functional/XmlRpcBasicTest.php
index 8737008..2a8b556 100644
--- a/tests/src/Functional/XmlRpcBasicTest.php
+++ b/tests/src/Functional/XmlRpcBasicTest.php
@@ -2,6 +2,9 @@
 
 namespace Drupal\Tests\xmlrpc\Functional;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
+use PHPUnit\Framework\Attributes\DataProvider;
 use GuzzleHttp\Exception\ClientException;
 
 /**
@@ -9,6 +12,8 @@ use GuzzleHttp\Exception\ClientException;
  *
  * @group xmlrpc
  */
+#[Group('xmlrpc')]
+#[RunTestsInSeparateProcesses]
 class XmlRpcBasicTest extends XmlRpcTestBase {
 
   /**
@@ -66,6 +71,7 @@ class XmlRpcBasicTest extends XmlRpcTestBase {
    *
    * @dataProvider noSignatureDataProvider
    */
+  #[DataProvider('noSignatureDataProvider')]
   public function testMethodSignatureOnMethodWithoutSignature(string $method) {
     // Install xmlrpc__test module.
     $this->container->get('module_installer')->install(['xmlrpc_test']);
diff --git a/tests/src/Functional/XmlRpcMessagesTest.php b/tests/src/Functional/XmlRpcMessagesTest.php
index 4e29beb..c71504c 100644
--- a/tests/src/Functional/XmlRpcMessagesTest.php
+++ b/tests/src/Functional/XmlRpcMessagesTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\xmlrpc\Functional;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\Component\Render\FormattableMarkup;
 use Drupal\xmlrpc\XmlRpcTrait;
 use GuzzleHttp\Exception\RequestException;
@@ -12,6 +14,8 @@ use GuzzleHttp\Psr7\Request;
  *
  * @group xmlrpc
  */
+#[Group('xmlrpc')]
+#[RunTestsInSeparateProcesses]
 class XmlRpcMessagesTest extends XmlRpcTestBase {
 
   use XmlRpcTrait;
diff --git a/tests/src/Functional/XmlRpcValidatorTest.php b/tests/src/Functional/XmlRpcValidatorTest.php
index 1558f33..57d160d 100644
--- a/tests/src/Functional/XmlRpcValidatorTest.php
+++ b/tests/src/Functional/XmlRpcValidatorTest.php
@@ -2,6 +2,9 @@
 
 namespace Drupal\Tests\xmlrpc\Functional;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
+
 /**
  * Test validation according to the XML-RPC.com validation suite.
  *
@@ -10,6 +13,8 @@ namespace Drupal\Tests\xmlrpc\Functional;
  *
  * @group xmlrpc
  */
+#[Group('xmlrpc')]
+#[RunTestsInSeparateProcesses]
 class XmlRpcValidatorTest extends XmlRpcTestBase {
 
   /**
@@ -74,7 +79,7 @@ class XmlRpcValidatorTest extends XmlRpcTestBase {
     $int_5 = mt_rand(-100, 100);
     $bool_5 = (($int_5 % 2) == 0);
     $string_5 = $this->randomMachineName();
-    $double_5 = (double) (mt_rand(-1000, 1000) / 100);
+    $double_5 = (float) (mt_rand(-1000, 1000) / 100);
     $time_5 = \Drupal::time()->getRequestTime();
     $base64_5 = $this->randomMachineName(100);
     $l_res_5 = xmlrpc_test_many_types_test($int_5, $bool_5, $string_5, $double_5, xmlrpc_date($time_5), $base64_5);
diff --git a/tests/src/Unit/ValueTest.php b/tests/src/Unit/ValueTest.php
index 5b42754..58d2c71 100644
--- a/tests/src/Unit/ValueTest.php
+++ b/tests/src/Unit/ValueTest.php
@@ -2,11 +2,15 @@
 
 namespace Drupal\Tests\xmlrpc\Unit;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\DataProvider;
+
 /**
  * Tests converting values to XML.
  *
  * @group xmlrpc
  */
+#[Group('xmlrpc')]
 class ValueTest extends XmlRpcUnitTestCase {
 
   /**
@@ -22,6 +26,7 @@ class ValueTest extends XmlRpcUnitTestCase {
    *
    * @dataProvider valueGetXmlProvider
    */
+  #[DataProvider('valueGetXmlProvider')]
   public function testValueGetXml($expected_xml, $value) {
     $xmlrpc_value = xmlrpc_value($value);
     $xml = xmlrpc_value_get_xml($xmlrpc_value);
diff --git a/xmlrpc.inc b/xmlrpc.inc
index 98fd466..9773df6 100644
--- a/xmlrpc.inc
+++ b/xmlrpc.inc
@@ -194,7 +194,6 @@ function xmlrpc_message_parse($xmlrpc_message) {
   if (!xml_parse($xmlrpc_message->_parser, $xmlrpc_message->message)) {
     return FALSE;
   }
-  xml_parser_free($xmlrpc_message->_parser);
 
   // Grab the error messages, if any.
   $xmlrpc_message = xmlrpc_message_get();
@@ -313,7 +312,7 @@ function xmlrpc_message_tag_close($parser, $tag) {
       break;
 
     case 'double':
-      $value = (double) trim($xmlrpc_message->current_tag_contents);
+      $value = (float) trim($xmlrpc_message->current_tag_contents);
       $value_flag = TRUE;
       break;
 
@@ -339,7 +338,7 @@ function xmlrpc_message_tag_close($parser, $tag) {
       break;
 
     case 'boolean':
-      $value = (boolean) trim($xmlrpc_message->current_tag_contents);
+      $value = (bool) trim($xmlrpc_message->current_tag_contents);
       $value_flag = TRUE;
       break;
 
diff --git a/xmlrpc.info.yml b/xmlrpc.info.yml
index f2a86f9..9b7d57c 100644
--- a/xmlrpc.info.yml
+++ b/xmlrpc.info.yml
@@ -1,4 +1,4 @@
-core_version_requirement: "^10 || ^11"
+core_version_requirement: ^10.1 || ^11 || ^12
 description: 'Provides XML-RPC client and server functionality.'
 name: 'XML-RPC'
 package: Web services
diff --git a/xmlrpc.module b/xmlrpc.module
index 6f5a476..4ad01f4 100644
--- a/xmlrpc.module
+++ b/xmlrpc.module
@@ -5,23 +5,15 @@
  * Enables XML-RPC functionality.
  */
 
-use Drupal\Core\Url;
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\xmlrpc\Hook\XmlrpcHooks;
 
 /**
  * Implements hook_help().
  */
+#[LegacyHook]
 function xmlrpc_help($route_name) {
-  switch ($route_name) {
-    case 'help.page.xmlrpc':
-      $output = '';
-      $output .= '<h3>' . t('About') . '</h3>';
-      $output .= '<p>' . t('The XML-RPC module gives external systems the opportunity to communicate with the site through the <a href=":url_definition">XML-RPC protocol</a>. An XML-RPC client can communicate with the site by making a request to <a href=":url_endpoint">xmlrpc</a>. For more information, see <a href=":url_docs">the online documentation for the XML-RPC module</a>.', [
-        ':url_definition' => 'https://en.wikipedia.org/wiki/XML-RPC',
-        ':url_endpoint' => Url::fromRoute('xmlrpc')->toString(),
-        ':url_docs' => 'https://drupal.org/documentation/modules/xmlrpc',
-      ]) . '</p>';
-      return $output;
-  }
+  return \Drupal::service(XmlrpcHooks::class)->help($route_name);
 }
 
 // phpcs:disable Drupal.NamingConventions.ValidFunctionName.InvalidPrefix
diff --git a/xmlrpc.services.yml b/xmlrpc.services.yml
new file mode 100644
index 0000000..5e53ddf
--- /dev/null
+++ b/xmlrpc.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\xmlrpc\Hook\XmlrpcHooks:
+    class: Drupal\xmlrpc\Hook\XmlrpcHooks
+    autowire: true
diff --git a/xmlrpc_example/src/Hook/XmlrpcExampleHooks.php b/xmlrpc_example/src/Hook/XmlrpcExampleHooks.php
new file mode 100644
index 0000000..b7bd347
--- /dev/null
+++ b/xmlrpc_example/src/Hook/XmlrpcExampleHooks.php
@@ -0,0 +1,172 @@
+<?php
+
+namespace Drupal\xmlrpc_example\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Hook implementations for xmlrpc_example.
+ */
+class XmlrpcExampleHooks {
+  use StringTranslationTrait;
+  /**
+   * @file
+   * Module file for xmlrpc_example module.
+   */
+  /**
+   * @defgroup xmlrpc_example Example: XML-RPC
+   * @ingroup examples
+   * @{
+   * Demonstration of XML-RPC in Drupal 8.
+   *
+   * This is an example of how to implement and XML-RPC server by registering
+   * callbacks to specific methods and how to make xmlrpc calls using the built-in
+   * xmlrpc() factory provided by the XML-RPC module.
+   *
+   * @see hook_xmlrpc()
+   * @see xmlrpc()
+   * @see xmlrpc_errno()
+   * @see xmlrpc_error_msg()
+   */
+
+  /**
+   * Implements hook_xmlrpc().
+   *
+   * Provides Drupal with an array to map XML-RPC callbacks to existing functions.
+   * These functions may be defined in other modules. The example implementation
+   * defines specific functions for the example services.
+   *
+   * This is the server part of the module, implementing a simple and little
+   * server with just two simple services. The server is divided in two
+   * different parts: the XML-RPC implementation (required) and a web form
+   * interface (optional) to configure some settings in the server side.
+   *
+   * The XMLRPC server will define two different services:
+   *
+   * - subtract: perform the subtraction of two numbers. The minimum and maximum
+   *   values returned by the server can be configured in the server configuration
+   *   form.
+   * - add: perform the addition of two numbers. The minimum and maximum values
+   *   returned by the server can be configured in the server configuration form.
+   *
+   * If the result value for the operation is over the maximum limit, a custom
+   * error number 10001 is returned. This is an arbitrary number and could be any
+   * number.
+   *
+   * If the result value for the operation is below the minimum limit, a custom
+   * error number 10002 is returned. Again, this value is arbitrary and could be
+   * any other number. Client applications must know the meaning of the error
+   * numbers returned by the server.
+   *
+   * The following code is the XML-RPC implementation of the server part.
+   * The first step is to define the methods. This methods should be associated
+   * to callbacks that will be defined later.
+   *
+   * Note: the XML-RPC server built in this module already includes several
+   * methods by default:
+   *
+   * Service discovery methods:
+   * - system.listMethods: return a list of the methods the server has, by name.
+   * - system.methodSignature: return a description of the argument format a
+   *   particular method expects.
+   * - system.methodHelp: returns a text description of a particular method.
+   *
+   * Other:
+   * - system.multicall: perform several method calls in a single XML-RPC request.
+   * - system.getCapabilities: determine if a given capability is supported.
+   *
+   * The methods defined by hook_xmlrpc() will be added to those provided by
+   * default by the XML-RPC server built in the module.
+   *
+   * @see hook_xmlrpc()
+   */
+  #[Hook('xmlrpc')]
+  public function xmlrpc() {
+    $methods[] = [
+          // First argument is the method name.
+      'xmlrpc_example.add',
+          // Callback to execute when this method is requested.
+      '_xmlrpc_example_server_add',
+          // An array defines the types of output and input values for this method.
+          [
+              // The first value is the return type, an integer in this case.
+            'int',
+              // First operand is an integer.
+            'int',
+              // Second operand is an integer.
+            'int',
+          ],
+          // Include a little description that is shown when XML-RPC server is
+          // requested for the implemented methods list.
+          // Method description.
+      $this->t('Returns the sum of the two arguments.'),
+    ];
+    // The subtract method is similar to the addition, only the method name,
+    // callback and description are different.
+    $methods[] = [
+      'xmlrpc_example.subtract',
+      '_xmlrpc_example_server_subtract',
+          [
+            'int',
+            'int',
+            'int',
+          ],
+      $this->t('Return difference of the two arguments.'),
+    ];
+    return $methods;
+  }
+
+  /**
+   * Implements hook_xmlrpc_alter().
+   *
+   * Check to see if xmlrpc_example.add and xmlrpc_example.subtract methods are
+   * defined and replace their callbacks with custom code.
+   *
+   * The alteration part of the module starts here. hook_xmlrpc_alter() is
+   * useful when you want to extend, limit or alter methods defined by other
+   * modules. This part is not required to have an XML-RPC server or client
+   * working, but is useful to understand what can we do using current xmlrpc
+   * API provided by drupal.
+   *
+   * This code can be defined in other module to alter the methods exposed by
+   * this xmlrpc demonstration server, or can be used to alter methods defined
+   * by other modules implementing hook_xmlrpc()
+   *
+   * As with the rest of the example module, an user interface is not required to
+   * make use of this hook. A configuration form is included to enable/disable
+   * this functionality, but this part is optional if you want to implement
+   * hook_xmlrpc_alter()
+   *
+   * This is the XML-RPC code for the alteration part. It will check if an option
+   * to enable the functionality is enabled and then alter it. We alter the
+   * 'xmlrpc_example.add' and 'xmlrpc_example.subtract' methods, changing the
+   * associated callback with custom functions. The modified methods (with
+   * new callbacks associated) will perform the addition or subtraction of the
+   * integer inputs, but will never check for limits nor return errors.
+   *
+   * @see hook_xmlrpc_alter()
+   */
+  #[Hook('xmlrpc_alter')]
+  public static function xmlrpcAlter(&$methods) {
+    $config = \Drupal::config('xmlrpc_example.server');
+    // Only perform alterations if instructed to do so.
+    if (!$config->get('alter_enabled')) {
+      return;
+    }
+    // Loop all defined methods (other modules may include additional methods).
+    foreach ($methods as $index => $method) {
+      // First element in the method array is the method name.
+      if ($method[0] == 'xmlrpc_example.add') {
+        // Replace current callback with custom callback
+        // (second argument of the array).
+        $methods[$index][1] = '_xmlrpc_example_alter_add';
+      }
+      // Do the same for the subtraction method.
+      if ($method[0] == 'xmlrpc_example.subtract') {
+        $methods[$index][1] = '_xmlrpc_example_alter_subtract';
+      }
+    }
+  }
+
+}
diff --git a/xmlrpc_example/tests/src/Functional/XmlRpcExampleTest.php b/xmlrpc_example/tests/src/Functional/XmlRpcExampleTest.php
index a818fd3..b24a5c7 100644
--- a/xmlrpc_example/tests/src/Functional/XmlRpcExampleTest.php
+++ b/xmlrpc_example/tests/src/Functional/XmlRpcExampleTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\xmlrpc_example\Functional;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\Tests\BrowserTestBase;
 use Drupal\xmlrpc\XmlRpcTrait;
 
@@ -13,6 +15,8 @@ use Drupal\xmlrpc\XmlRpcTrait;
  *
  * @group xmlrpc
  */
+#[Group('xmlrpc')]
+#[RunTestsInSeparateProcesses]
 class XmlRpcExampleTest extends BrowserTestBase {
 
   use XmlRpcTrait;
diff --git a/xmlrpc_example/xmlrpc_example.info.yml b/xmlrpc_example/xmlrpc_example.info.yml
index 74b7acc..d07b7b8 100644
--- a/xmlrpc_example/xmlrpc_example.info.yml
+++ b/xmlrpc_example/xmlrpc_example.info.yml
@@ -1,4 +1,4 @@
-core_version_requirement: "^10 || ^11"
+core_version_requirement: ^10.1 || ^11 || ^12
 description: 'An example of how to implement client and server communications using XML-RPC.'
 name: 'XML-RPC Example'
 package: Example modules
diff --git a/xmlrpc_example/xmlrpc_example.module b/xmlrpc_example/xmlrpc_example.module
index 25349cc..599184b 100644
--- a/xmlrpc_example/xmlrpc_example.module
+++ b/xmlrpc_example/xmlrpc_example.module
@@ -1,5 +1,12 @@
 <?php
 
+/**
+ * @file
+ */
+
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\xmlrpc_example\Hook\XmlrpcExampleHooks;
+
 /**
  * @file
  * Module file for xmlrpc_example module.
@@ -72,37 +79,9 @@
  *
  * @see hook_xmlrpc()
  */
+#[LegacyHook]
 function xmlrpc_example_xmlrpc() {
-  $methods[] = [
-    // First argument is the method name.
-    'xmlrpc_example.add',
-    // Callback to execute when this method is requested.
-    '_xmlrpc_example_server_add',
-    // An array defines the types of output and input values for this method.
-    [
-      // The first value is the return type, an integer in this case.
-      'int',
-      // First operand is an integer.
-      'int',
-      // Second operand is an integer.
-      'int',
-    ],
-    // Include a little description that is shown when XML-RPC server is
-    // requested for the implemented methods list.
-    // Method description.
-    t('Returns the sum of the two arguments.'),
-  ];
-
-  // The subtract method is similar to the addition, only the method name,
-  // callback and description are different.
-  $methods[] = [
-    'xmlrpc_example.subtract',
-    '_xmlrpc_example_server_subtract',
-    ['int', 'int', 'int'],
-    t('Return difference of the two arguments.'),
-  ];
-
-  return $methods;
+  return \Drupal::service(XmlrpcExampleHooks::class)->xmlrpc();
 }
 
 /**
@@ -217,26 +196,9 @@ function _xmlrpc_example_server_subtract($num1, $num2) {
  *
  * @see hook_xmlrpc_alter()
  */
+#[LegacyHook]
 function xmlrpc_example_xmlrpc_alter(&$methods) {
-  $config = \Drupal::config('xmlrpc_example.server');
-
-  // Only perform alterations if instructed to do so.
-  if (!$config->get('alter_enabled')) {
-    return;
-  }
-  // Loop all defined methods (other modules may include additional methods).
-  foreach ($methods as $index => $method) {
-    // First element in the method array is the method name.
-    if ($method[0] == 'xmlrpc_example.add') {
-      // Replace current callback with custom callback
-      // (second argument of the array).
-      $methods[$index][1] = '_xmlrpc_example_alter_add';
-    }
-    // Do the same for the subtraction method.
-    if ($method[0] == 'xmlrpc_example.subtract') {
-      $methods[$index][1] = '_xmlrpc_example_alter_subtract';
-    }
-  }
+  \Drupal::service(XmlrpcExampleHooks::class)->xmlrpcAlter($methods);
 }
 
 /**
diff --git a/xmlrpc_example/xmlrpc_example.services.yml b/xmlrpc_example/xmlrpc_example.services.yml
new file mode 100644
index 0000000..41cdd12
--- /dev/null
+++ b/xmlrpc_example/xmlrpc_example.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\xmlrpc_example\Hook\XmlrpcExampleHooks:
+    class: Drupal\xmlrpc_example\Hook\XmlrpcExampleHooks
+    autowire: true
