Change record status: 
Project: 
Introduced in branch: 
10.0.x
Introduced in version: 
10.0.0
Description: 

Before this change, custom account cancellation methods were implemented via hook_user_cancel(). However, this method wasn't reliable for the reasons described in #3135592: Cannot implement a custom user cancellation method. This change deprecates the hook_user_cancel() hook and it schedules for removal in the next major Drupal release.

Third-party code that need to act when an account is cancelled, should listen to the \Drupal\user\Event\AccountCancelEvent event and add its own cancellation logic. There are cases when the third-party module wants to avoid the default Drupal core's handling of account cancellation. In this case, the custom subscriber should, act before the core subscribers, by using a proper priority and should use AccountCancelEvent::stopPropagation() method to avoid downstream subscribers.

Currently, Drupal core exposes the following account cancellation subscribers:

Subscriber Priority
\Drupal\comment\EventSubscriber\CommentAccountCancelSubscriber::onUserAccountCancel() 20
\Drupal\history\EventSubscriber\HistoryAccountCancelSubscriber::onUserAccountCancel() 20
\Drupal\node\EventSubscriber\NodeAccountCancelSubscriber::onUserAccountCancel() 20
\Drupal\user\EventSubscriber\AccountCancelSubscriber::::onUserAccountCancel() 0

Example

In the following example we pretend that third-party code has defined a custom account cancellation method custom_user_cancel, via hook_user_cancel_methods_alter():

Before

/**
 * Implements hook_user_cancel().
 */
function mymodule_user_cancel(array $edit, UserInterface $account, string $method): void {
  if ($method === 'custom_user_cancel') {
    \Drupal::messenger()->addStatus('Custom user cancel method executed.');
  }
}

But note that, because of the bug from #3135592: Cannot implement a custom user cancellation method, the logic of user_cancel_block cancellation method is also executed.

After

mymodule.services.yml:

services:
  mymodule.account_cancel:
    class: Drupal\mymodule\EventSubscriber\MyModuleAccountCancelSubscriber
    tags:
      - { name: event_subscriber }

Drupal\mymodule\EventSubscriber\MyModuleAccountCancelSubscriber:

class MyModuleAccountCancelSubscriber implements EventSubscriberInterface {

  public static function getSubscribedEvents(): array {
    return [
      // Act before all Drupal core account cancellation subscribers.
      AccountCancelEvent::class => ['onAccountCancel' => 50],
    ];
  }

  public function onUserAccountCancel(AccountCancelEvent $event): void {
    if ($event->getMethod() === 'custom_user_cancel') {
      // Do own logic here.
      \Drupal::messenger()->addStatus('Custom user cancel method executed.');
      // Prevent *all* Drupal core account cancellation subscribers to act.
      $event->stopPropagation();
    }
  }

}

Other changes

Constructor of UserController, UserCancelForm and UserMultipleCancelConfirm classes should receive the user.account_cancellation service as an additional parameter. Not passing this service as parameter is deprecated and the parameter will be mandatory in the next Drupal major version.

Impacts: 
Module developers