$impl) + $implementations; } } /** * Implements hook_menu(). */ function composer_manager_menu() { $items = array(); $items['admin/config/system/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/config/system/composer-manager/packages'] = array( 'title' => 'Packages', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items['admin/config/system/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_modules_enabled(). * * @see composer_manager_write_if_changed() */ function composer_manager_modules_enabled($modules) { composer_manager_write_if_changed($modules); } /** * Implements hook_modules_disabled(). * * @see composer_manager_write_if_changed() */ function composer_manager_modules_disabled($modules) { 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. 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 = &drupal_static(__FUNCTION__, FALSE); if (variable_get('composer_manager_autobuild_file', 1)) { 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 7.x-1.6 https://www.drupal.org/node/2297413 * * @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. * * @return \Composer\Autoload\ClassLoader() * The ClassLoader instance. */ function composer_manager_register_autoloader() { static $registered = FALSE; $autoloader = composer_manager_vendor_dir() . '/autoload.php'; if (!$registered) { if (!file_exists($autoloader)) { $message = t('Autoloader not found: @file', array('@file' => $autoloader)); throw new \RuntimeException($message); } $registered = TRUE; } return 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 = composer_manager_file_dir(); 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_exception('composer_manager', $e); } lock_release(__FUNCTION__); return $success; } /** * Returns the path to the vendor directory. * * @return string */ function composer_manager_vendor_dir() { // Don't break sites that upgraded from <= 7.x-1.0-beta5. composer_manager_beta5_compatibility(); $vendor_dir = variable_get('composer_manager_vendor_dir', 'sites/all/vendor'); $is_absolute = (0 === strpos($vendor_dir, '/')); if (!$is_absolute) { $vendor_dir = DRUPAL_ROOT . '/' . $vendor_dir; } return $vendor_dir; } /** * Ensures that sites don't break after upgrading from <= 7.x-1.0-beta5. * * In versions <= 7.x-1.0-beta5, the default vendor directory was defined as * "sites/all/libraries/composer'". In versions > 7.x-1.0-beta5, the default * vendor directory is "sites/all/vendor". This check ensures that the vendor * directory doesn't unexpectedly change for people who upgraded the module from * an earlier version and haven't changed any of the default settings. * * Composer Manager explicitly sets the "composer_manager_vendor_dir" to * "sites/all/vendor" during installation, so we know that the module was * upgraded if the variable isn't set. */ function composer_manager_beta5_compatibility() { if (NULL === ($default = variable_get('composer_manager_vendor_dir', NULL))) { // Set the variable to the old default so it doesn't change unexpectedly. variable_set('composer_manager_vendor_dir', 'sites/all/libraries/composer'); } } /** * Return the URI to the composer.lock file. * * @return string|bool * The URI to the composer.lock file, usually in public://. Returns FALSE if * the lock file cannot be read. */ function composer_manager_lock_file() { $dir_uri = composer_manager_file_dir(); $lock_file = $dir_uri . '/composer.lock'; // Make sure we can read composer.lock and it's valid. if (file_exists($lock_file) && is_readable($lock_file)) { $json = file_get_contents($lock_file); $decoded = drupal_json_decode($json); if (!is_array($decoded)) { return FALSE; } } else { return FALSE; } return $lock_file; } /** * Returns the realpath to the Composer file directory. * * @return string * * @throws \RuntimeException */ function composer_manager_file_dir() { module_load_include('inc', 'composer_manager', 'composer_manager.writer'); $scheme = file_default_scheme(); // Composer can only be run on a locally mounted file system. If the scheme is // set to something different like S3, we fall back to the public scheme. if (!in_array($scheme, array('public', 'private'))) { $scheme = 'public'; } $dir_uri = variable_get('composer_manager_file_dir', $scheme . '://composer'); if (!composer_manager_prepare_directory($dir_uri)) { throw new \RuntimeException(t('Error creating directory: @dir', array('@dir' => $dir_uri))); } if (!$realpath = drupal_realpath($dir_uri)) { throw new \RuntimeException(t('Error resolving directory: @dir', array('@dir' => $dir_uri))); } return $realpath; }