You are here

process.inc in Module Builder 7

Same filename and directory in other branches
  1. 6.2 includes/process.inc

Module builder code processing code.

Turns downloaded data and shipped templates into data about hooks.

File

includes/process.inc
View source
<?php

/**
 * @file
 * Module builder code processing code.
 * 
 * Turns downloaded data and shipped templates into data about hooks.
 */

/**
 * Get stored hook declarations, keyed by hook name, with destination.
 *
 * @return
 *   An array of the form:
 *     [hook_action_info] => 
 *       'declaration' => function hook_action_info()
 *       'destination' => function hook_action_info()
 */
function module_builder_get_hook_declarations($dir = NULL) {
  $data = module_builder_get_hook_data($dir);
  foreach ($data as $group => $hooks) {
    foreach ($hooks as $key => $hook) {
      $return[$hook['name']] = array(
        'declaration' => $hook['definition'],
        'destination' => $hook['destination'],
      );
    }
  }
  return $return;
}

/**
 * Get just hook declarations, keyed by hook name.
 *
 * @return
 *   An array of the form:
 *     [hook_action_info] => function hook_action_info()
 */
function module_builder_get_hook_declarations_plain($dir = NULL) {
  $data = module_builder_get_hook_data($dir);
  foreach ($data as $group => $hooks) {
    foreach ($hooks as $key => $hook) {
      $return[$hook['name']] = $hook['definition'];
    }
  }
  return $return;
}

/**
 * Get just hook names.
 *
 * @param $short
 *   Whether to return hook names as just 'init' or 'hook_init'.
 *   Might as well call like this: module_builder_get_hook_names('short') for clarity.
 * @return
 *   A flat array of strings.
 */
function module_builder_get_hook_names($dir = NULL, $short = FALSE) {
  $data = module_builder_get_hook_data_flat($dir);
  $names = array_keys($data);
  if ($short) {
    foreach ($names as $key => $hook_name) {
      $names[$key] = str_replace('hook_', '', $hook_name);
    }
  }
  return $names;
}

/**
 * Helper for API functions that don't care about file grouping.
 */
function module_builder_get_hook_data_flat($dir = NULL) {
  $data = module_builder_get_hook_data($dir);
  foreach ($data as $group => $hooks) {
    foreach ($hooks as $key => $hook) {
      $return[$hook['name']] = $hook;
    }
  }
  return $return;
}

/**
 * Retrieve hook data from storage file.
 */
function module_builder_get_hook_data($directory = NULL) {
  if (!isset($directory)) {

    //$directory = file_create_path(variable_get('module_builder_hooks_directory', 'hooks'));
    $directory = _module_builder_get_hooks_directory();
  }
  $hooks_file = "{$directory}/hooks_processed.php";
  if (file_exists($hooks_file)) {
    return unserialize(file_get_contents($hooks_file));
  }
}

/**
 * Get the timestamp of the processed file.
 */
function module_builder_get_hook_data_last_updated($directory = NULL) {
  if (!isset($directory)) {
    $directory = _module_builder_get_hooks_directory();
  }
  $hooks_file = "{$directory}/hooks_processed.php";
  if (file_exists($hooks_file)) {
    $timestamp = filemtime($hooks_file);
    return format_date($timestamp, 'large');
  }
}

/**
* Builds complete hook data array from downloaded files and stores in a file.
*
* @param hook_file_data
*  An array of data about the files to process, keyed by (safe) filename:
   [MODULE.FILENAME] => Array // eg system.core.php
     [path] => full path to the file
     [destination] => %module.module
     [group] => GROUP  // eg core
     [hook_destinations] => array(%module.foo => hook_foo, etc)
*  This is the same format as returned by update.inc.
* @return
*  An array keyed by originating file of the following form:
*     [GROUP] => array(  // grouping for UI.
        [{i}] => array(
          [name] => hook_foo
          [definition] => function hook_foo($node, $teaser = FALSE, $page = FALSE)
          [description] => Description.
          [destination] => Destination module file for hook code from this file.
*/
function module_builder_process_hook_data($hook_file_data) {

  /*
  // Get list of hook documentation files
  $files = module_builder_get_doc_files($directory);
  if (!isset($files)) {
    return NULL;
  }
  */

  //print_r($hook_file_data);

  // check file_exists?
  // Build list of hooks
  $hook_groups = array();
  foreach ($hook_file_data as $file => $file_data) {
    $hook_data_raw = _module_builder_process_hook_file($file_data['path']);
    $file_name = basename($file, '.php');
    $group = $file_data['group'];

    // Create an array in the form of:
    // array(
    //   'filename' => array(
    //     array('hook' => 'hook_foo', 'description' => 'hook_foo description'),
    //     ...
    //   ),
    //   ...
    // );
    foreach ($hook_data_raw['names'] as $key => $hook) {

      // The destination is possibly specified per-hook; if not, then given
      // for the whole file.
      if (isset($file_data['hook_destinations'][$hook])) {
        $destination = $file_data['hook_destinations'][$hook];
      }
      else {
        $destination = $file_data['destination'];
      }
      $hook_groups[$group][$key] = array(
        'name' => $hook,
        'definition' => $hook_data_raw['definitions'][$key],
        'description' => $hook_data_raw['descriptions'][$key],
        'destination' => $destination,
      );

      //dsm($hook_groups);
    }

    // foreach hook_data
  }

  // foreach files

  //dsm($hook_groups);

  //print_r($hook_groups);

  // Write the processed data to a file.
  $directory = _module_builder_get_hooks_directory();
  file_put_contents("{$directory}/hooks_processed.php", serialize($hook_groups));
  return $hook_groups;
}

/**
 * Retrieve list of documentation files containing hook definitions.
 *
 * @return array
 *   Array of files
 */
function module_builder_get_doc_files() {
  $dir = _module_builder_get_hooks_directory();
  if (!$dir) {
    drupal_set_message(t('Please configure the hook documentation path in <a href="!settings">module builder settings</a>.', array(
      '!settings' => url('admin/settings/module_builder'),
    )), 'error');
    return NULL;
  }
  $files = array();
  if (is_dir($dir)) {
    if ($dh = opendir($dir)) {
      while (($file = readdir($dh)) !== FALSE) {

        // Ignore files that don't make sense to include
        // TODO: replace all the .foo with one of the arcane PHP string checking functions
        if (!in_array($file, array(
          '.',
          '..',
          '.DS_Store',
          'CVS',
          'hooks_processed.php',
        ))) {
          $files[] = $file;
        }
      }
      closedir($dh);
    }
    else {
      drupal_set_message(t('There was an error opening the hook documentation path. Please try again.'), 'error');
      return NULL;
    }
  }
  else {
    drupal_set_message(t('Hook documentation path is invalid. Please return to the <a href="!settings">module builder settings</a> page to try again.', array(
      '!settings' => url('admin/settings/module_builder'),
    )), 'error');
    return NULL;
  }
  return $files;
}

/**
 * Extracts raw hook data from downloaded hook documentation files.
 *
 * @param string $path
 *   Path to hook file
 * @param string $file
 *   Name of hook file
 * @return array
 *   Array of hook data:
 *    [0]: Each hook's user-friendly description
 *    [1]: Each hook's entire function declaration: "function name($params)"
 *    [2]: Name of each hook
 */
function _module_builder_process_hook_file($filepath) {
  $contents = file_get_contents("{$filepath}");

  // Get the destination stored at the top of the file.
  preg_match('[^// module builder: (\\S+)\\n]', $contents, $matches_dest);
  $destination = $matches_dest[1];

  // The pattern for extracting function data: capture first line of doc,
  // function declaration, and hook name.
  $pattern = '[
      / \\* \\* \\n    # start phpdoc
      \\ \\* \\ ( .* ) \\n  # first line of phpdoc: capture the text
  (?: \\ \\* .* \\n )* # lines of phpdoc
      \\ \\* /  \\n    # end phpdoc
    ( function \\ ( hook_\\w* ) .* ) \\  { # function declaration: capture both entire declaration and name
  ]mx';
  preg_match_all($pattern, $contents, $matches);

  // We don't care about the full matches.

  //array_shift($matches);
  $data = array(
    'descriptions' => $matches[1],
    'definitions' => $matches[2],
    'names' => $matches[3],
    'destination' => $destination,
  );
  return $data;
}

/**
 * Parse a module_builder template file.
 *
 * Template files are composed of several sections in the form of:
 *
 * == START [title of template section] ==
 * [the body of the template section]
 * == END ==
 *
 * @param string $file
 *   The template file to parse
 * @return Array
 *   Return array keyed by hook name, whose values are of the form: 
 *    array('template' => TEMPLATE BODY)
 */
function module_builder_parse_template($file) {
  $data = array();

  // Captures a template name and body from a template file.
  $pattern = '#== START (.*?) ==(.*?)== END ==#ms';
  preg_match_all($pattern, $file, $matches);
  $count = count($matches[0]);
  for ($i = 0; $i < $count; $i++) {
    $data[$matches[1][$i]] = array(
      #'title' => $matches[1][$i],
      'template' => $matches[2][$i],
    );

    /*
    $hook_custom_declarations[] = array(
      'title' => $matches[1][$i],
      'data' => $matches[2][$i]
    );
    */
  }
  return $data;
}

Functions

Namesort descending Description
module_builder_get_doc_files Retrieve list of documentation files containing hook definitions.
module_builder_get_hook_data Retrieve hook data from storage file.
module_builder_get_hook_data_flat Helper for API functions that don't care about file grouping.
module_builder_get_hook_data_last_updated Get the timestamp of the processed file.
module_builder_get_hook_declarations Get stored hook declarations, keyed by hook name, with destination.
module_builder_get_hook_declarations_plain Get just hook declarations, keyed by hook name.
module_builder_get_hook_names Get just hook names.
module_builder_parse_template Parse a module_builder template file.
module_builder_process_hook_data Builds complete hook data array from downloaded files and stores in a file.
_module_builder_process_hook_file Extracts raw hook data from downloaded hook documentation files.