function ThemeHookCollectorPass::collectThemeHookImplementations

Same name and namespace in other branches
  1. 11.x core/lib/Drupal/Core/Hook/ThemeHookCollectorPass.php \Drupal\Core\Hook\ThemeHookCollectorPass::collectThemeHookImplementations()

Collects procedural and Attribute hook implementations.

Parameters

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

string $theme: The name of the theme.

string $currentThemePreg: A regular expression matching only the theme being scanned.

string $allThemesPreg: A regular expression matching every theme, longer theme names are matched first.

bool $shouldSkipProceduralScan: Skip the procedural check for the current theme.

File

core/lib/Drupal/Core/Hook/ThemeHookCollectorPass.php, line 225

Class

ThemeHookCollectorPass
Collects and registers hook implementations.

Namespace

Drupal\Core\Hook

Code

protected function collectThemeHookImplementations($dir, $theme, $currentThemePreg, $allThemesPreg, bool $shouldSkipProceduralScan) : void {
  $hookFileCache = FileCacheFactory::get('theme_hook_implementations');
  $proceduralHookFileCache = FileCacheFactory::get('theme_procedural_hook_implementations:' . $allThemesPreg);
  foreach ($this->getHookFileIterator($dir, [
    "{$theme}.theme",
    'theme-settings.php',
  ]) as $fileinfo) {
    assert($fileinfo instanceof \SplFileInfo);
    $fileExtension = $fileinfo->getExtension();
    $filename = $fileinfo->getPathname();
    $isThemeSettings = str_ends_with($filename, 'theme-settings.php');
    if ($fileExtension === 'theme') {
      $this->deprecatedThemeFiles[pathinfo($filename, PATHINFO_FILENAME)] = TRUE;
    }
    if ($isThemeSettings) {
      @trigger_error('Using a theme-settings.php file in the ' . $theme . ' theme is deprecated in drupal:11.5.0 and is removed from drupal:12.0.0. Convert the hooks to use attributes or move to the .theme file. See https://www.drupal.org/node/3587273', E_USER_DEPRECATED);
    }
    if ($fileExtension === 'php' && !$isThemeSettings) {
      $cached = $hookFileCache->get($filename);
      if ($cached) {
        $class = $cached['class'];
        $attributes = $cached['attributes'];
      }
      else {
        // Immediately after a deployment, opcache may not yet have refreshed
        // depending on the value of opcache.revalidation_freq which defaults
        // to 2 seconds. Ensure that reflection operates on the new code by
        // forcibly invalidating the opcode cache.
        // @see https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.revalidate-freq
        OpCodeCache::invalidate($filename);
        $namespace = preg_replace('#^src/#', "Drupal/{$theme}/", substr($fileinfo->getPath(), strlen($dir) + 1));
        $class = $namespace . '/' . $fileinfo->getBasename('.php');
        $class = str_replace('/', '\\', $class);
        $attributes = [];
        if (class_exists($class)) {
          $reflectionClass = new \ReflectionClass($class);
          $attributes = self::getAttributeInstances($reflectionClass);
          $hookFileCache->set($filename, [
            'class' => $class,
            'attributes' => $attributes,
          ]);
        }
      }
      foreach ($attributes as $method => $methodAttributes) {
        foreach ($methodAttributes as $attribute) {
          if ($attribute instanceof Hook) {
            self::checkInvalidHookParametersInThemes($attribute, $class);
            $this->oopImplementations[$attribute->hook][$class . '::' . ($attribute->method ?: $method)] = $theme;
          }
          elseif ($attribute instanceof RemoveHook) {
            throw new \LogicException("The #[RemoveHook] attribute is not allowed in themes. Found in {$class}.");
          }
          elseif ($attribute instanceof ReorderHook) {
            throw new \LogicException("The #[ReorderHook] attribute is not allowed in themes. Found in {$class}.");
          }
        }
      }
    }
    elseif (!$shouldSkipProceduralScan) {
      $implementations = $proceduralHookFileCache->get($filename);
      if ($implementations === NULL) {
        $finder = MockFileFinder::create($filename);
        $parser = new StaticReflectionParser('', $finder);
        $implementations = [
          'hooks' => [],
        ];
        foreach ($parser->getMethodAttributes() as $function => $attributes) {
          if (StaticReflectionParser::hasAttribute($attributes, ExtensionFileIsConverted::class)) {
            $implementations['@skip_theme_file_deprecation'] = TRUE;
          }
          if (StaticReflectionParser::hasAttribute($attributes, ProceduralHookScanStop::class)) {
            break;

          }
          if (!StaticReflectionParser::hasAttribute($attributes, LegacyHook::class) && (preg_match($currentThemePreg, $function, $matches) || preg_match($allThemesPreg, $function, $matches))) {
            assert($function === $matches['theme'] . '_' . $matches['hook']);
            $implementations['hooks'][] = [
              'theme' => $matches['theme'],
              'hook' => $matches['hook'],
            ];
          }
        }
        $proceduralHookFileCache->set($filename, $implementations);
      }
      if (isset($implementations['@skip_theme_file_deprecation'])) {
        unset($this->deprecatedThemeFiles[pathinfo($filename, PATHINFO_FILENAME)]);
      }
      foreach ($implementations['hooks'] as $implementation) {
        $this->proceduralImplementations[$implementation['hook']][] = $implementation['theme'];
      }
    }
  }
}

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