function HookCollectorPass::collectModuleHookImplementations

Collects procedural and Attribute hook implementations.

Parameters

string $dir: The directory in which the module resides.

string $module: The name of the module.

string $module_preg: A regular expression matching every module, longer module names are matched first.

bool $skip_procedural: Skip the procedural check for the current module.

File

core/lib/Drupal/Core/Hook/HookCollectorPass.php, line 393

Class

HookCollectorPass
Collects and registers hook implementations.

Namespace

Drupal\Core\Hook

Code

protected function collectModuleHookImplementations($dir, $module, $module_preg, bool $skip_procedural) : void {
  $hook_file_cache = FileCacheFactory::get('hook_implementations');
  $procedural_hook_file_cache = FileCacheFactory::get('procedural_hook_implementations:' . $module_preg);
  $iterator = new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS | \FilesystemIterator::FOLLOW_SYMLINKS);
  $iterator = new \RecursiveCallbackFilterIterator($iterator, static::filterIterator(...));
  $iterator = new \RecursiveIteratorIterator($iterator);
  /** @var \RecursiveDirectoryIterator | \RecursiveIteratorIterator $iterator*/
  foreach ($iterator as $fileinfo) {
    assert($fileinfo instanceof \SplFileInfo);
    $extension = $fileinfo->getExtension();
    $filename = $fileinfo->getPathname();
    if ($extension === 'php') {
      $cached = $hook_file_cache->get($filename);
      if ($cached) {
        $class = $cached['class'];
        $attributes = $cached['attributes'];
      }
      else {
        $namespace = preg_replace('#^src/#', "Drupal/{$module}/", $iterator->getSubPath());
        $class = $namespace . '/' . $fileinfo->getBasename('.php');
        $class = str_replace('/', '\\', $class);
        $attributes = [];
        if (class_exists($class)) {
          $reflectionClass = new \ReflectionClass($class);
          $attributes = self::getAttributeInstances($reflectionClass);
          $hook_file_cache->set($filename, [
            'class' => $class,
            'attributes' => $attributes,
          ]);
        }
      }
      foreach ($attributes as $method => $methodAttributes) {
        foreach ($methodAttributes as $attribute) {
          if ($attribute instanceof Hook) {
            self::checkForProceduralOnlyHooks($attribute, $class);
            $this->oopImplementations[$attribute->hook][$class . '::' . ($attribute->method ?: $method)] = $attribute->module ?? $module;
            if ($attribute->order !== NULL) {
              // Use a lower weight for order operations that are declared
              // together with the hook listener they apply to.
              $this->orderOperations[$attribute->hook][0][] = $attribute->order
                ->getOperation("{$class}::{$method}");
            }
          }
          elseif ($attribute instanceof ReorderHook) {
            // Use a higher weight for order operations that target other hook
            // listeners.
            $this->orderOperations[$attribute->hook][1][] = $attribute->order
              ->getOperation($attribute->class . '::' . $attribute->method);
          }
          elseif ($attribute instanceof RemoveHook) {
            $this->removeHookIdentifiers[$attribute->hook][] = $attribute->class . '::' . $attribute->method;
          }
        }
      }
    }
    elseif (!$skip_procedural) {
      $implementations = $procedural_hook_file_cache->get($filename);
      if ($implementations === NULL) {
        $finder = MockFileFinder::create($filename);
        $parser = new StaticReflectionParser('', $finder);
        $implementations = [];
        foreach ($parser->getMethodAttributes() as $function => $attributes) {
          if (StaticReflectionParser::hasAttribute($attributes, ProceduralHookScanStop::class)) {
            break;

          }
          if (!StaticReflectionParser::hasAttribute($attributes, LegacyHook::class) && preg_match($module_preg, $function, $matches) && !StaticReflectionParser::hasAttribute($attributes, LegacyModuleImplementsAlter::class)) {
            assert($function === $matches['module'] . '_' . $matches['hook']);
            $implementations[] = [
              'module' => $matches['module'],
              'hook' => $matches['hook'],
            ];
          }
        }
        $procedural_hook_file_cache->set($filename, $implementations);
      }
      foreach ($implementations as $implementation) {
        $this->addProceduralImplementation($fileinfo, $implementation['hook'], $implementation['module']);
      }
    }
    if ($extension === 'inc') {
      $parts = explode('.', $fileinfo->getFilename());
      if (count($parts) === 3 && $parts[0] === $module) {
        $this->groupIncludes[$parts[1]][] = $filename;
      }
    }
  }
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.