You are here

xautoload.module in X Autoload 7.2

File

xautoload.module
View source
<?php

/*
 * When the module has just been installed,
 * Drupal does not know yet this is a boot-level module.
 *
 * We can not rely on hook_boot() to fire, and instead register the autoloader
 * on inclusion of this *.module file.
 */
_xautoload_register();

// Hook implementations
// -----------------------------------------------------------------------------

/**
 * Implements hook_boot()
 *
 * This is only to let Drupal know we want this module to load in bootstrap.
 */
function xautoload_boot() {
  $finder = xautoload_get_finder();
  $api = new xautoload_InjectedAPI_hookXautoload($finder);
  _simpletest_xautoload($api);
}

/**
 * Implements hook_xautoload() on behalf of simpletest
 */
function _simpletest_xautoload($api) {
  if (version_compare(PHP_VERSION, '5.3') < 0) {

    // Namespaces only exist since PHP 5.3.
    return;
  }

  // Register a plugin that can find classes within disabled modules.
  // The plugin will only load classes of the format "Drupal\(module)\Tests\.."
  $api
    ->namespacePlugin('Drupal', new xautoload_Plugin_DrupalSimpletest());
}

/**
 * Implements hook_custom_theme()
 * We only do this because that's the first hook to fire after bootstrap.
 */
function xautoload_custom_theme() {

  // Make sure this only runs once.
  // (we run this from hook_init also, to avoid upgrade issues)
  static $first_run = TRUE;
  if (!$first_run) {
    return;
  }
  $first_run = FALSE;

  // Tell the "extension system" that the "main phase" has started, and thus
  // all Drupal modules are now available.
  xautoload('drupalExtensionSystem')
    ->mainPhase();

  // Let other modules register stuff to the finder.
  $api = new xautoload_InjectedAPI_hookXautoload(xautoload_get_finder());
  foreach (module_implements('xautoload') as $module) {
    $api
      ->setModule($module);
    $f = $module . '_xautoload';
    $f($api);
  }
}

/**
 * Implements hook_init()
 *
 * Note:
 *   This is a first step to allow modules to register foreign namespaces.
 *   We will probably change this, to allow bootstrap modules to register their
 *   namespaces earlier in the request.
 *   We might also find a solution to cache the result of this hook between
 *   requests. This would require a different implementation of the InjectedAPI,
 *   which would no longer have a direct reference to the finder object.
 */
function xautoload_init() {

  // If hook_custom_theme() hasn't been triggered, we call it now.
  xautoload_custom_theme();
}

/**
 * Implements hook_modules_enabled()
 */
function xautoload_modules_enabled($modules) {
  xautoload('drupalExtensionSystem')
    ->addModules($modules);
}

/**
 * Implements hook_modules_installed()
 */
function xautoload_modules_installed($modules) {
  xautoload('drupalExtensionSystem')
    ->addModules($modules);
}

/**
 * Implements hook_simpletest_alter().
 */
function xautoload_simpletest_alter(&$groups) {
  if (version_compare(PHP_VERSION, '5.3') < 0) {

    // Namespaces only exist since PHP 5.3.
    return;
  }

  // Select all PSR-0 classes in the Tests namespace of all modules.
  // This does include disabled modules.
  $system_list = db_query("SELECT name, filename FROM {system}")
    ->fetchAllKeyed();
  foreach ($system_list as $name => $filename) {

    // Build directory in which the test files would reside.
    $tests_dir = DRUPAL_ROOT . '/' . dirname($filename) . '/lib/Drupal/' . $name . '/Tests';

    // Scan it for test files if it exists.
    if (is_dir($tests_dir)) {
      $files = file_scan_directory($tests_dir, '/.*\\.php/');
      if (!empty($files)) {
        $basedir = DRUPAL_ROOT . '/' . dirname($filename) . '/lib/';
        foreach ($files as $file) {

          // Convert the file name into the namespaced class name.
          $replacements = array(
            '/' => '\\',
            $basedir => '',
            '.php' => '',
          );
          $classes[] = strtr($file->uri, $replacements);
        }
      }
    }
  }

  // Check that each class has a getInfo() method and store the information
  // in an array keyed with the group specified in the test information.
  foreach ($classes as $class) {

    // Test classes need to implement getInfo() to be valid.
    if (class_exists($class) && method_exists($class, 'getInfo')) {
      $info = call_user_func(array(
        $class,
        'getInfo',
      ));

      // If this test class requires a non-existing module, skip it.
      if (!empty($info['dependencies'])) {
        foreach ($info['dependencies'] as $module) {
          if (!drupal_get_filename('module', $module)) {
            continue 2;
          }
        }
      }
      $groups[$info['group']][$class] = $info;
    }
  }

  // Sort the groups and tests within the groups by name.
  uksort($groups, 'strnatcasecmp');
  foreach ($groups as $group => &$tests) {
    uksort($tests, 'strnatcasecmp');
  }
}

/**
 * Implements hook_registry_files_alter()
 *
 * Support wildcard syntax in the files[] setting in your module's info file.
 */
function xautoload_registry_files_alter(&$files, $modules) {
  $orig = $files;

  // The class file is loaded using the regular uncached xautoload autoload.
  $rec_scan = new xautoload_RegistryWildcard_RecursiveScan($files);
  foreach ($files as $path => $file) {
    $rec_scan
      ->check($path, $file);
  }
}

/**
 * Implements hook_module_implements_alter()
 */
function xautoload_module_implements_alter(&$implementations, $hook) {
  if ($hook === 'init' || $hook === 'custom_theme') {

    // Move xautoload_$hook() to the start.
    $implementations = array(
      'xautoload' => FALSE,
    ) + $implementations;
  }
}

// Hooks on behalf of other modules
// -----------------------------------------------------------------------------

/**
 * Implements hook_xautoload on behalf of libraries module
 */
function libraries_xautoload($api) {
  if (!function_exists('libraries_info')) {

    // Libraries is at a lower version, which does not have this function.
    return;
  }
  foreach (libraries_info() as $name => $info) {
    if (isset($info['xautoload'])) {
      $xinfo = $info['xautoload'];
      $api
        ->setLibrary($name);
      if (is_callable($xinfo)) {
        call_user_func($xinfo, $api);
      }
    }
  }
}

// Public API functions.
// -----------------------------------------------------------------------------

/**
 * Get the class finder object.
 * This is the public version of _xautoload_finder().
 */
function xautoload_get_finder() {

  // Get it from the registry.
  return xautoload('classFinder');
}

/**
 * Get a service object from the registry.
 * Services are lazy-created first time you need them.
 *
 * @param string $key
 *   Identifier of the service within the registry.
 *   The xautoload_ServiceFactory should have a method with the same name.
 */
function xautoload($key) {
  static $service_registry;
  if (!isset($service_registry)) {
    $service_factory = new xautoload_ServiceFactory();
    $service_registry = new xautoload_ServiceRegistry($service_factory);
  }
  return $service_registry
    ->get($key);
}

// "Private" functions.
// -----------------------------------------------------------------------------

/**
 * Build and register the xautoload loader.
 */
function _xautoload_register() {

  // Check that this runs only once.
  static $_first_run = TRUE;
  if (!$_first_run) {
    return;
  }
  $_first_run = FALSE;

  // Register a temporary solution.
  spl_autoload_register('_xautoload_autoload_temp');

  // This one class needs to be loaded manually.
  // Just believe me on that one.
  _xautoload_autoload_temp('xautoload_InjectedAPI_findFile');

  // Register the "real" class loader.
  xautoload('classLoader')
    ->register(TRUE);

  // The "extension system" needs some babysitting.
  // Let's start by filling it with the boot-level modules.
  xautoload('drupalExtensionSystem')
    ->bootstrapPhase();

  // Unregister our temporary solution.
  spl_autoload_unregister('_xautoload_autoload_temp');

  // if xautoload has just been enabled, it won't be listed as a boot-level
  // module. To be sure, we register its namespace explicitly.
  if (!module_exists('xautoload') && function_exists('drupal_get_path')) {
    $lib_dir = drupal_get_path('module', 'xautoload') . '/lib';
    xautoload('classFinder')
      ->registerPrefixDeep('xautoload', $lib_dir);
  }

  // Let's crash right here if it doesn't work.
  new xautoload_InjectedAPI_hookXautoload(xautoload('classFinder'));
}

/**
 * Temporary loader callback, to avoid any module_load_include()
 * while building the real autoloader.
 *
 * @param string $name
 *   Name of the class or interface we want to load.
 */
function _xautoload_autoload_temp($name) {
  if (preg_match('#^xautoload_(.*)$#', $name, $m)) {

    // This is boot time, drupal_get_path() is not available yet.
    $file = dirname(__FILE__) . '/lib/' . strtr($m[1], '_', '/') . '.php';
    require_once $file;
    if (!class_exists($name, FALSE) && !interface_exists($name, FALSE)) {
      throw new Exception("Class {$name} not found in {$file}.");
    }
  }
}

Functions

Namesort descending Description
libraries_xautoload Implements hook_xautoload on behalf of libraries module
xautoload Get a service object from the registry. Services are lazy-created first time you need them.
xautoload_boot Implements hook_boot()
xautoload_custom_theme Implements hook_custom_theme() We only do this because that's the first hook to fire after bootstrap.
xautoload_get_finder Get the class finder object. This is the public version of _xautoload_finder().
xautoload_init Implements hook_init()
xautoload_modules_enabled Implements hook_modules_enabled()
xautoload_modules_installed Implements hook_modules_installed()
xautoload_module_implements_alter Implements hook_module_implements_alter()
xautoload_registry_files_alter Implements hook_registry_files_alter()
xautoload_simpletest_alter Implements hook_simpletest_alter().
_simpletest_xautoload Implements hook_xautoload() on behalf of simpletest
_xautoload_autoload_temp Temporary loader callback, to avoid any module_load_include() while building the real autoloader.
_xautoload_register Build and register the xautoload loader.