You are here

composer_manager.module in Composer Manager 6

Provides consolidated management of third-party Composer-compatible packages required by contributed modules.

File

composer_manager.module
View source
<?php

/**
 * @file
 * Provides consolidated management of third-party Composer-compatible packages
 * required by contributed modules.
 */

/**
 * Implements hook_init().
 */
function composer_manager_init() {
  try {
    composer_manager_register_autoloader();
  } catch (\RuntimeException $e) {

    // @see https://api.drupal.org/api/drupal/includes%21bootstrap.inc/function/drupal_is_cli/7
    if (!(!isset($_SERVER['SERVER_SOFTWARE']) && (php_sapi_name() == 'cli' || is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0))) {
      watchdog('composer_manager', $e
        ->getMessage(), array(), WATCHDOG_ERROR);
    }
  }
}

/**
 * Implements hook_menu().
 */
function composer_manager_menu() {
  $items = array();
  $items['admin/settings/composer-manager'] = array(
    'title' => 'Composer Manager',
    'description' => 'View the status of packages managed by Composer and configure the location of the composer.json file and verdor directory.',
    'page callback' => 'composer_manager_packages_page',
    'access arguments' => array(
      'administer site configuration',
    ),
    'file' => 'composer_manager.admin.inc',
  );
  $items['admin/settings/composer-manager/packages'] = array(
    'title' => 'Packages',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );
  $items['admin/settings/composer-manager/settings'] = array(
    'title' => 'Settings',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'composer_manager_settings_form',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'file' => 'composer_manager.admin.inc',
    'type' => MENU_LOCAL_TASK,
  );
  return $items;
}

/**
 * Implements hook_form_FORM_ID_alter() for system_modules().
 */
function composer_manager_form_system_modules_alter(&$form, &$form_state) {
  $form['#submit'][] = 'composer_manager_system_modules_submit';
  $form['#module_list'] = module_list();
}

/**
 * Form submission handler for system_modules().
 *
 * Determines what modules have been enabled or disabled and rewrites the
 * consolidated composer.json file if one of the modules has dependencies.
 *
 * @see composer_manager_write_if_changed()
 */
function composer_manager_system_modules_submit($form, &$form_state) {
  $module_list = module_list();

  // Get list of modules that have been enabled or disabled.
  $enabled = array_diff_key($module_list, $form['#module_list']);
  $disabled = array_diff_key($form['#module_list'], $module_list);
  $modules = array_merge($enabled, $disabled);
  composer_manager_write_if_changed($modules);
}

/**
 * Writes the composer.json file if one of the enabled / disabled modules
 * has a composer.json file.
 *
 * This is a primitive check to ensure that the composer.json file is built only
 * when it has changes. This check always passes when run via the command line,
 * as it is assumed that Drush is being used to enable or disable the the
 * modules. A static boolean is also set flagging whether one or more modules
 * being acted on contains a composer.json file, which is used in Drush hooks.
 *
 * @param array $modules
 *   The enabled / disabled modules being scanned for a composer.json file.
 */
function composer_manager_write_if_changed(array $modules) {
  $changed =& composer_manager_static(__FUNCTION__, FALSE);
  if (variable_get('composer_manager_autobuild_file', 1) || drupal_is_cli()) {
    if (composer_manager_packages_have_changed($modules)) {
      $changed = TRUE;
      composer_manager_write_file();
    }
  }
}

/**
 * Returns TRUE if at least one passed modules has a composer.json file or
 * implements hook_composer_json_alter(). These conditions indicate that the
 * consolidated composer.json file has likely changed.
 *
 * @param array $modules
 *   The list of modules being scanned for composer.json files, usually a list
 *   of modules that were installed or uninstalled.
 *
 * @return bool
 */
function composer_manager_packages_have_changed(array $modules) {
  foreach ($modules as $module) {

    // Check if the module has a composer.json file.
    $module_path = drupal_get_path('module', $module);
    if (file_exists($module_path . '/composer.json')) {
      return TRUE;
    }

    // Check if the module implements hook_composer_json_alter().
    if (module_hook($module, 'composer_json_alter')) {
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * Returns TRUE if at least one module has a composer.json file.
 *
 * @param array $modules
 *   An array of modules being checked.
 *
 * @return boolean
 *
 * @deprecated since 6.x-1.6 https://www.drupal.org/node/2297415
 *
 * @see composer_manager_packages_have_changed()
 */
function composer_manager_has_composer_file(array $modules) {
  foreach ($modules as $module) {
    $module_path = drupal_get_path('module', $module);
    if (file_exists($module_path . '/composer.json')) {
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * Registers the autoloader for all third-party packages.
 */
function composer_manager_register_autoloader() {
  static $registered = FALSE;
  if (!$registered) {
    $autoloader = composer_manager_vendor_dir() . '/autoload.php';
    if (!file_exists($autoloader)) {
      $message = t('Autoloader not found: @file', array(
        '@file' => $autoloader,
      ));
      throw new \RuntimeException($message);
    }
    $registered = TRUE;
    require $autoloader;
  }
}

/**
 * Writes the consolidated composer.json file for all modules that require
 * third-party packages managed by Composer.
 *
 * @return bool
 */
function composer_manager_write_file() {

  // Ensure only one process runs at a time. 10 seconds is more than enough.
  // It is rare that a conflict will happen, and it isn't mission critical that
  // we wait for the lock to release and regenerate the file again.
  if (!lock_acquire(__FUNCTION__, 10)) {
    return FALSE;
  }
  require_once __DIR__ . '/composer_manager.writer.inc';
  try {
    $data = composer_manager_fetch_data();
    $json = composer_manager_build_json($data);
    if ($json) {
      $dir_uri = variable_get('composer_manager_file_dir', file_directory_path() . '/composer');
      composer_manager_put_file($dir_uri, $json);
    }
    $success = TRUE;
  } catch (\RuntimeException $e) {
    $success = FALSE;
    if (user_access('administer site configuration')) {
      drupal_set_message(t('Error writing composer.json file'), 'error');
    }
    watchdog('composer_manager', $e
      ->getMessage(), array(), WATCHDOG_ERROR);
  }
  lock_release(__FUNCTION__);
  return $success;
}

/**
 * Returns the path to the vendor directory.
 *
 * @return string
 */
function composer_manager_vendor_dir() {
  $vendor_dir = variable_get('composer_manager_vendor_dir', 'sites/all/vendor');
  $is_absolute = 0 === strpos($vendor_dir, '/');
  if (!$is_absolute) {
    $vendor_dir = getcwd() . '/' . $vendor_dir;
  }
  return $vendor_dir;
}

/**
 * @see https://api.drupal.org/api/drupal/includes%21bootstrap.inc/function/drupal_static/7
 */
function &composer_manager_static($name, $default_value = NULL, $reset = FALSE) {
  static $data = array(), $default = array();

  // First check if dealing with a previously defined static variable.
  if (isset($data[$name]) || array_key_exists($name, $data)) {

    // Non-NULL $name and both $data[$name] and $default[$name] statics exist.
    if ($reset) {

      // Reset pre-existing static variable to its default value.
      $data[$name] = $default[$name];
    }
    return $data[$name];
  }

  // Neither $data[$name] nor $default[$name] static variables exist.
  if (isset($name)) {
    if ($reset) {

      // Reset was called before a default is set and yet a variable must be
      // returned.
      return $data;
    }

    // First call with new non-NULL $name. Initialize a new static variable.
    $default[$name] = $data[$name] = $default_value;
    return $data[$name];
  }

  // Reset all: ($name == NULL). This needs to be done one at a time so that
  // references returned by earlier invocations of drupal_static() also get
  // reset.
  foreach ($default as $name => $value) {
    $data[$name] = $value;
  }

  // As the function returns a reference, the return should always be a
  // variable.
  return $data;
}

Functions

Namesort descending Description
composer_manager_form_system_modules_alter Implements hook_form_FORM_ID_alter() for system_modules().
composer_manager_has_composer_file Deprecated Returns TRUE if at least one module has a composer.json file.
composer_manager_init Implements hook_init().
composer_manager_menu Implements hook_menu().
composer_manager_packages_have_changed Returns TRUE if at least one passed modules has a composer.json file or implements hook_composer_json_alter(). These conditions indicate that the consolidated composer.json file has likely changed.
composer_manager_register_autoloader Registers the autoloader for all third-party packages.
composer_manager_static
composer_manager_system_modules_submit Form submission handler for system_modules().
composer_manager_vendor_dir Returns the path to the vendor directory.
composer_manager_write_file Writes the consolidated composer.json file for all modules that require third-party packages managed by Composer.
composer_manager_write_if_changed Writes the composer.json file if one of the enabled / disabled modules has a composer.json file.