I've been checking some of my code for D9 compability, and have struggled to find a tool that will flag up the use of deprecated hooks - I was specifically looking at these Search API hooks which are marked as deprecated, but there must be plenty of others that currently aren't detectable automatically: https://git.drupalcode.org/project/search_api/-/blob/8.x-1.x/search_api..... OO code and core hooks seem well covered, but I couldn't configure anything to flag up contrib despite it being annotated with @deprecated.

I raised this as an issue in #d9readiness during the porting weekend, and it was suggested this would be the most relevant place to raise an issue. Let me know if you need any more details. Thanks.

Comments

ben.kyriakou created an issue. See original summary.

gábor hojtsy’s picture

I don't have time ATM to work on this, but here is a brain dump for people with interest to help :)

We could find implementations of a specific hook with https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Plugin%21... so the harder question is how do we find deprecated hooks in the first place to check.

Can we assume projects being scanned will always define hook_projectname_*? If we could, we could php include all *.api.php files and get defined functions to filter them. I am not sure we can assume the naming convention given that some projects define multiple subextensions and some projects don't use the same machine name for the extension as the project. So we would probably need to find all *.api.php files in the project scanned and tokenize them (https://www.php.net/manual/en/book.tokenizer.php) to find which ones have an @deprecated annotation. It is possible that instead of raw PHP tokenizer, Doctrine's annotation parsed can be used for this, see https://www.doctrine-project.org/projects/doctrine-annotations/en/latest... -- or if that is not good, we also rely on nikic/php-parser, which is used for hook_theme() checking in particular already.

Once we have a list of deprecated hooks to check in a project, we can use HookDiscovery in the runtime to find implementations. I don't know if we can find invocations of the hook though, that is not very likely to happen given that hook invocations usually happen only in the extensions that define them. Most hooks are not defined as a thing to be invoked by whoever wants to invoke them.

gambry’s picture

Title: Add ability to detect deprecated contrib hooks » Add ability to detect deprecated hooks
Issue summary: View changes
Status: Active » Needs work

I gave it a try during the weekend. The first thing is I believe this issue should address all deprecated hooks, and not only contrib.
For the sake of D9 readiness is actually more important to flag core's ones usage, but of course worth covering core and contrib in one go.

Then I struggle to find a good library doing both code parsing and extracting annotation values.

- Doctrine handles mainly classes and methods, and doesn't really handle nicely php files and functions.
- I moved to use straight Drupal\Component\Annotation\Doctrine\DocParse, which would be ideal as already shipped within core, but I can't find a way to fetch the @deprecation tag description. The parses is expecting @tag() annotations, so with opening and closing brackets. In their absence, the annotation value is ignored. We could extend this and overrides some methods, but I'd like to follow the Not-Invented-Here philosophy.
- I then moved to phpdocumentor/reflection-docblock, part of phpdocumentor, which does the docblock processing. We still need to get the actual hook function name, but we can use tokenizer for that.

So below the outcome of my test script. If anyone has better ideas please share!

include 'vendor/autoload.php';

// This can be in a loop of i.e. glob(DRUPAL_PATH . '/*.api.php');
$content = file_get_contents('web/core/modules/path/path.api.php');

// Tokenize the content.
$tokens = token_get_all($content);
$factory  = \phpDocumentor\Reflection\DocBlockFactory::createInstance();

// Process all tokens.
foreach ($tokens as $i => $token) {

  // Skip 1-character tokens.
  if (is_string($token)) {
    continue;
  }

  // Process DocBlocks, looking for @deprecated tags.
  if ($token[0] === T_DOC_COMMENT) {
    try {
      $docblock = $factory->create($token[1]);
    } catch (InvalidArgumentException $e) {
      // Ignore malformed docblocks, i.e. "@{"
      continue;
    }

    // Process @deprecated tag.
    $tags = $docblock->getTagsByName('deprecated');
    if ($tag = array_shift($tags)) {
      $deprecation = $tag->render();

      // Next token is a new line and right after should be the deprecated hook
      // example function, prefixed with "hook_"
      if ($tokens[$i + 2][0] === T_FUNCTION && strpos($tokens[$i + 4][1], 'hook_') === 0) {
        $hook = substr($tokens[$i + 4][1], 5);

        // Do whatever you need with the hook.
      }
    }
  }
}
millnut’s picture

We've just hit this doing an upgrade from Drupal 9.5 to Drupal 10 where deprecated hooks are not flagged in upgrade status.

Is there any recent update on progress with getting upgrade status to detected deprecated hooks?

Failing that is there a way to get a full list of all hooks deprecated in 9.x and removed in 10?

fanton’s picture

In Drupal 9, there are only 4 deprecated hooks (you can verify this here):

- hook_field_widget_form_alter
- hook_field_widget_multivalue_form_alter
- hook_field_widget_multivalue_WIDGET_TYPE_form_alter
- hook_field_widget_WIDGET_TYPE_form_alter

While it would be great to have automatic detection of deprecated hooks, since this feature is not available for now, it should be mentioned in the Upgrade Status feature list so that users are aware that they need to verify it manually.

gábor hojtsy’s picture

Issue tags: +phpstan-drupal

See https://github.com/mglaman/phpstan-drupal/issues/126 for discussion on doing this in phpstan-drupal. I relayed your info @fanton to there. While we could write one-off code for these in Upgrade Status, it would be more universal to cover it in phpstan-drupal.

gábor hojtsy’s picture

Status: Needs work » Active
jnicola’s picture

Just ran into this issue, thankfully we have really good test coverage that caught the issue.

Specifically our issue was with hook_field_widget_WIDGET_TYPE_form_alter

Google for "Drupal 10 deprecated hooks" does not yield any useful information on this topic either.