ppf-1.2.x-dev/ppf.module

ppf.module
<?php

/**
 * @file
 * Module file for the Preprocessor Files module.
 *
 * Allows Drupal to load preprocessor files, similar to how templates are
 * discovered and loaded.
 *
 * e.g. For 'node--page.html.twig', the system will load a respective
 * 'node--page.preprocess.php'. Variables preprocessed in that function will
 * be available in the respective template. The file functions exactly like a
 * traditional preprocess hook.
 */

use Drupal\Core\Url;
use Drupal\ppf\PreprocessorFiles;

/**
 * Implements hook_help().
 *
 * @noinspection PhpUnused
 */
function ppf_help(string $route_name) : ?string {
  $settingsUrl = Url::fromRoute('ppf.settings')->toString();
  switch ($route_name) {
    case 'ppf.settings':
      $output = '<p>' . t('The Preprocessor Files module allows you to create dedicated files to preprocess template variables instead of hooks.') . '</p>';
      $output .= '<p><strong>' . t('Refer to the official <a href="https://git.drupalcode.org/project/ppf#preprocess-files">README</a> for more detailed documentation!') . '</strong></p>';
      return $output;

    case 'help.page.ppf':
      $output = '<p><strong>' . t('Refer to the official <a href="https://git.drupalcode.org/project/ppf#preprocess-files">README</a> for more detailed documentation!') . '</strong></p>';
      $output .= '<p>' . t('The Preprocessor Files module allows you to create dedicated files to preprocess template variables instead of hooks.') . '</p>';
      $output .= '<h2>' . t('Quickstart') . '</h2>';
      $output .= '<p>' . t('Visit the <a href="@settingsUrl">configurations page</a> to customize the module as you see fit. On the settings page, you can generate a starter folder in your default theme.') . '</p>';
      $output .= '<p>' . t('Copy the <code>YOUR_THEME/preprocess/.HOOK.preprocess.php</code> to create a new <code>YOUR_THEME/preprocess/node.preprocess.php</code>.') . '</p>';
      $output .= '<p><em>' . t('If you changed the default configurations, adjust the directory name and file extension accordingly.') . '</em></p>';
      $output .= '<p>' . t('When visiting a <code>node.html.twig</code> template, you should now be able to see any variables modifications done within your new preprocessor file.') . '</p>';
      $output .= '<h2>' . t('Main Functionality') . '</h2>';
      $output .= '<p>' . t('This behaviour is similar to <a href="https://www.drupal.org/docs/8/theming-drupal-8/modifying-attributes-in-a-theme-file">Preprocess Functions</a>. Understanding how they work is crucial to the use of this module.') . '</p>';
      $output .= '<p>' . t('For example, for the <code>node.html.twig</code> template, you would create a <code>YOUR_THEME_preprocess_node()</code> function in your theme to preprocess template variables.') . '</p>';
      $output .= '<p>' . t('With this module, you can create a <code>node.preprocess.php</code> file to preprocess template variables instead.') . '</p>';
      $output .= '<p>' . t('This file can be placed in your theme at <code>YOUR_THEME/preprocess/node.preprocess.php</code> and the contents of the file can be treated as if you were in a preprocess hook.') . '</p>';
      $output .= '<h2>' . t('Configuration') . '</h2>';
      $output .= '<p>' . t('Visit the <a href="@settingsUrl">configurations page</a> to alter settings for the module.', ['@settingsUrl' => $settingsUrl]) . '</p>';
      $output .= '<p>' . t('Here you can alter the directory the preprocessor files are loaded in, as well as the file extension for preprocessor files.') . '</p>';

      return $output;
  }

  return NULL;
}

/**
 * Implements hook_theme_registry_alter().
 */
function ppf_theme_registry_alter(&$theme_registry) : void {
  // Get all files.
  $allFiles = [
    'theme' => PreprocessorFiles::getPreprocessorFilesForTheme(),
    'modules' => PreprocessorFiles::getPreprocessorFilesForActiveModules(),
  ];

  // Loop in our hooks.
  foreach ($allFiles as $origin => $files) {
    foreach ($files as $hook => $filePaths) {
      // If the hook isn't in the registry, we try to find a base hook, so we
      // can instantiate it.
      if (!isset($theme_registry[$hook])) {
        // This aims to emulate the same functionality as preprocess functions.
        // e.g. If node--article.html.twig doesn't exist, but a
        // HOOK_preprocess_node__article() function exists, a registry entry is
        // created for the 'node__article' hook. We need to cover the case where
        // a preprocessor file exists, but no template or hook exists.
        $baseHookCandidate = explode("__", $hook)[0];

        // If no base hook exists, we simply stop here.
        if (!isset($theme_registry[$baseHookCandidate])) {
          continue;
        }

        // Now we know a base hook exists.
        // We simply want to copy the implementation of the base hook into a new
        // entry for our subhook.
        $theme_registry[$hook] = $theme_registry[$baseHookCandidate];

        // Adjust the data for our subhook.
        $theme_registry[$hook]['base hook'] = $baseHookCandidate;
      }

      // Insert custom preprocess functions for base hook if we need to.
      if (isset($theme_registry[$hook]['base hook'])) {
        // Insert custom preprocess function of the base hook.
        _ppf_insert_hook_preprocess_functions($theme_registry, $hook, TRUE);
      }

      // Insert custom preprocess functions for hook.
      _ppf_insert_hook_preprocess_functions($theme_registry, $hook);

      // Entries with base hooks should inherit any preprocessor files.
      if (
        isset($theme_registry[$hook]['base hook'])
        && isset($theme_registry[$theme_registry[$hook]['base hook']]['ppf']['preprocess files'])
      ) {
        $theme_registry[$hook]['ppf']['base preprocess files'] = $theme_registry[$theme_registry[$hook]['base hook']]['ppf']['preprocess files'];
      }

      // Add our preprocessor file to the list of files.
      foreach ($filePaths as $file) {
        $theme_registry[$hook]['ppf']['preprocess files'][$origin][] = $file;
      }
    }
  }
}

/**
 * Add custom functions to the 'preprocess functions' entry in the registry.
 *
 * @param array $theme_registry
 *   The Drupal theme registry.
 * @param string $hook
 *   The hook to add our custom function to.
 * @param bool $base
 *   Set to TRUE if processing the base hook of the hook.
 */
function _ppf_insert_hook_preprocess_functions(array &$theme_registry, string $hook, bool $base = FALSE) : void {
  $themeImplementation = '_ppf_load_hook_theme_preprocessor_files';
  $modulesImplementation = '_ppf_load_hook_modules_preprocessor_files';
  $check = $hook;
  if ($base) {
    $themeImplementation = '_ppf_load_base_hook_theme_preprocessor_files';
    $modulesImplementation = '_ppf_load_base_hook_modules_preprocessor_files';
    $check = $theme_registry[$hook]['base hook'];
  }

  // For preprocessor files loaded from the theme, execute them last, or after
  // the last traditional theme preprocess found.
  if (!in_array($themeImplementation, $theme_registry[$hook]['preprocess functions'])) {
    if (!$base || !isset($theme_registry[$hook]['base hook'])) {
      $theme_registry[$hook]['preprocess functions'][] = $themeImplementation;
    }
    else {
      // Get the active theme and base themes.
      $activeTheme = PreprocessorFiles::service()->themeManager->getActiveTheme();
      $baseThemes = $activeTheme->getBaseThemeExtensions();

      // We'll do some shenanigans here to accomplish this.
      $themeList = array_reverse(array_merge([$activeTheme->getName() => $activeTheme], $baseThemes));

      // Here we want to find the last theme preprocess function.
      $lastThemePreprocess = NULL;
      foreach ($themeList as $themeName => $extension) {
        $foundThemePreprocess = array_filter($theme_registry[$hook]['preprocess functions'], function ($entry) use ($themeName, $check) {
          return str_starts_with($entry, $themeName . '_preprocess') && str_ends_with($entry, 'preprocess_' . $check);
        });
        if (!empty($foundThemePreprocess)) {
          $lastThemePreprocess = end($foundThemePreprocess);
        }
      }

      // Place plugin preprocessing right after theme preprocessing.
      // Otherwise, we try something different.
      if ($lastThemePreprocess !== NULL) {
        $key = array_search($lastThemePreprocess, $theme_registry[$hook]['preprocess functions']);
        $theme_registry[$hook]['preprocess functions'] = _ppf_array_insert_after($theme_registry[$hook]['preprocess functions'], $key, [$themeImplementation => $themeImplementation]);
      }
      else {
        $theme_registry[$hook]['preprocess functions'][] = $themeImplementation;
      }
    }
  }

  // For preprocessor files loaded from modules, execute them after traditional
  // module hooks, but before theme hooks.
  if (!in_array($modulesImplementation, $theme_registry[$hook]['preprocess functions'])) {
    // We'll do some shenanigans here to accomplish this.
    $moduleList = PreprocessorFiles::service()->moduleHandler->getModuleList();

    // Here we want to find the last module preprocess function.
    $lastModulePreprocess = NULL;
    foreach ($moduleList as $moduleName => $extension) {
      $foundModulePreprocess = array_filter($theme_registry[$hook]['preprocess functions'], function ($entry) use ($moduleName, $check) {
        return str_starts_with($entry, $moduleName . '_preprocess') && str_ends_with($entry, 'preprocess_' . $check);
      });
      if (!empty($foundModulePreprocess)) {
        $lastModulePreprocess = end($foundModulePreprocess);
      }
    }

    // Place plugin preprocessing right before theme preprocessing.
    // Otherwise, we try something different.
    if ($lastModulePreprocess !== NULL) {
      $key = array_search($lastModulePreprocess, $theme_registry[$hook]['preprocess functions']);
      $theme_registry[$hook]['preprocess functions'] = _ppf_array_insert_after($theme_registry[$hook]['preprocess functions'], $key, [$modulesImplementation => $modulesImplementation]);
    }
    else {
      // If a module preprocess could not be located, we try to place it
      // after the traditional 'template_preprocess'.
      // We go further than that if 'template_preprocess_HOOK' exists.
      $templatePreprocessKey = array_search('template_preprocess', $theme_registry[$hook]['preprocess functions']);
      $templatePreprocessHookKey = array_search('template_preprocess_' . $hook, $theme_registry[$hook]['preprocess functions']);
      $templatePreprocessBaseHookKey = FALSE;
      if (isset($theme_registry[$hook]['base hook'])) {
        $templatePreprocessBaseHookKey = array_search('template_preprocess_' . $hook, $theme_registry[$hook]['preprocess functions']);
      }

      if ($templatePreprocessHookKey !== FALSE) {
        $key = array_search($templatePreprocessHookKey, $theme_registry[$hook]['preprocess functions']);
        $theme_registry[$hook]['preprocess functions'] = _ppf_array_insert_after($theme_registry[$hook]['preprocess functions'], $key, [$modulesImplementation => $modulesImplementation]);
      }
      elseif (isset($theme_registry[$hook]['base hook']) && $templatePreprocessBaseHookKey !== FALSE) {
        $key = array_search($templatePreprocessBaseHookKey, $theme_registry[$hook]['preprocess functions']);
        $theme_registry[$hook]['preprocess functions'] = _ppf_array_insert_after($theme_registry[$hook]['preprocess functions'], $key, [$modulesImplementation => $modulesImplementation]);
      }
      else {
        $key = array_search($templatePreprocessKey, $theme_registry[$hook]['preprocess functions']);
        $theme_registry[$hook]['preprocess functions'] = _ppf_array_insert_after($theme_registry[$hook]['preprocess functions'], $key, [$modulesImplementation => $modulesImplementation]);
      }
    }
  }
}

/**
 * Handle loading of relevant preprocessor file for a given template.
 *
 * @param array $variables
 *   The array of variables passed to the Twig template.
 * @param string $hook
 *   The theme hook.
 * @param array $info
 *   The theme info.
 */
function _ppf_load_hook_theme_preprocessor_files(array &$variables, string $hook, array $info) : void {
  if (isset($info['ppf']['preprocess files']['theme'])) {
    foreach ($info['ppf']['preprocess files']['theme'] as $file) {
      if (file_exists($file)) {
        include $file;
      }
    }
  }
}

/**
 * Handle loading of relevant preprocessor file for a given template.
 *
 * @param array $variables
 *   The array of variables passed to the Twig template.
 * @param string $hook
 *   The theme hook.
 * @param array $info
 *   The theme info.
 */
function _ppf_load_hook_modules_preprocessor_files(array &$variables, string $hook, array $info) : void {
  if (isset($info['ppf']['preprocess files']['modules'])) {
    foreach ($info['ppf']['preprocess files']['modules'] as $file) {
      if (file_exists($file)) {
        include $file;
      }
    }
  }
}

/**
 * Handle loading of relevant preprocessor file for a given template.
 *
 * @param array $variables
 *   The array of variables passed to the Twig template.
 * @param string $hook
 *   The theme hook.
 * @param array $info
 *   The theme info.
 */
function _ppf_load_base_hook_theme_preprocessor_files(array &$variables, string $hook, array $info) : void {
  if (isset($info['ppf']['base preprocess files']['theme'])) {
    foreach ($info['ppf']['base preprocess files']['theme'] as $file) {
      if (file_exists($file)) {
        include $file;
      }
    }
  }
}

/**
 * Handle loading of relevant preprocessor file for a given template.
 *
 * @param array $variables
 *   The array of variables passed to the Twig template.
 * @param string $hook
 *   The theme hook.
 * @param array $info
 *   The theme info.
 */
function _ppf_load_base_hook_modules_preprocessor_files(array &$variables, string $hook, array $info) : void {
  if (isset($info['ppf']['base preprocess files']['modules'])) {
    foreach ($info['ppf']['base preprocess files']['modules'] as $file) {
      if (file_exists($file)) {
        include $file;
      }
    }
  }
}

/**
 * Insert a value or key/value pair after a specific key in an array.
 *
 * If key doesn't exist, value is appended to the end of the array.
 *
 * @param array $array
 * @param string $key
 * @param array $new
 *
 * @return array
 */
function _ppf_array_insert_after(array $array, string $key, array $new) : array {
  $keys = array_keys( $array );
  $index = array_search( $key, $keys );
  $pos = false === $index ? count( $array ) : $index + 1;

  return array_merge( array_slice( $array, 0, $pos ), $new, array_slice( $array, $pos ) );
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc