cms/drupal/includes/registry.inc
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * @file
       
     5  * This file contains the code registry parser engine.
       
     6  */
       
     7 
       
     8 /**
       
     9  * @defgroup registry Code registry
       
    10  * @{
       
    11  * The code registry engine.
       
    12  *
       
    13  * Drupal maintains an internal registry of all interfaces or classes in the
       
    14  * system, allowing it to lazy-load code files as needed (reducing the amount
       
    15  * of code that must be parsed on each request).
       
    16  */
       
    17 
       
    18 /**
       
    19  * Does the work for registry_update().
       
    20  */
       
    21 function _registry_update() {
       
    22 
       
    23   // The registry serves as a central autoloader for all classes, including
       
    24   // the database query builders. However, the registry rebuild process
       
    25   // requires write ability to the database, which means having access to the
       
    26   // query builders that require the registry in order to be loaded. That
       
    27   // causes a fatal race condition. Therefore we manually include the
       
    28   // appropriate query builders for the currently active database before the
       
    29   // registry rebuild process runs.
       
    30   $connection_info = Database::getConnectionInfo();
       
    31   $driver = $connection_info['default']['driver'];
       
    32   require_once DRUPAL_ROOT . '/includes/database/query.inc';
       
    33   require_once DRUPAL_ROOT . '/includes/database/select.inc';
       
    34   require_once DRUPAL_ROOT . '/includes/database/' . $driver . '/query.inc';
       
    35 
       
    36   // Get current list of modules and their files.
       
    37   $modules = db_query("SELECT * FROM {system} WHERE type = 'module'")->fetchAll();
       
    38   // Get the list of files we are going to parse.
       
    39   $files = array();
       
    40   foreach ($modules as &$module) {
       
    41     $module->info = unserialize($module->info);
       
    42     $dir = dirname($module->filename);
       
    43 
       
    44     // Store the module directory for use in hook_registry_files_alter().
       
    45     $module->dir = $dir;
       
    46 
       
    47     if ($module->status) {
       
    48       // Add files for enabled modules to the registry.
       
    49       foreach ($module->info['files'] as $file) {
       
    50         $files["$dir/$file"] = array('module' => $module->name, 'weight' => $module->weight);
       
    51       }
       
    52     }
       
    53   }
       
    54   foreach (file_scan_directory('includes', '/\.inc$/') as $filename => $file) {
       
    55     $files["$filename"] = array('module' => '', 'weight' => 0);
       
    56   }
       
    57 
       
    58   $transaction = db_transaction();
       
    59   try {
       
    60     // Allow modules to manually modify the list of files before the registry
       
    61     // parses them. The $modules array provides the .info file information, which
       
    62     // includes the list of files registered to each module. Any files in the
       
    63     // list can then be added to the list of files that the registry will parse,
       
    64     // or modify attributes of a file.
       
    65     drupal_alter('registry_files', $files, $modules);
       
    66     foreach (registry_get_parsed_files() as $filename => $file) {
       
    67       // Add the hash for those files we have already parsed.
       
    68       if (isset($files[$filename])) {
       
    69         $files[$filename]['hash'] = $file['hash'];
       
    70       }
       
    71       else {
       
    72         // Flush the registry of resources in files that are no longer on disc
       
    73         // or are in files that no installed modules require to be parsed.
       
    74         db_delete('registry')
       
    75           ->condition('filename', $filename)
       
    76           ->execute();
       
    77         db_delete('registry_file')
       
    78           ->condition('filename', $filename)
       
    79           ->execute();
       
    80       }
       
    81     }
       
    82     $parsed_files = _registry_parse_files($files);
       
    83 
       
    84     $unchanged_resources = array();
       
    85     $lookup_cache = array();
       
    86     if ($cache = cache_get('lookup_cache', 'cache_bootstrap')) {
       
    87       $lookup_cache = $cache->data;
       
    88     }
       
    89     foreach ($lookup_cache as $key => $file) {
       
    90       // If the file for this cached resource is carried over unchanged from
       
    91       // the last registry build, then we can safely re-cache it.
       
    92       if ($file && in_array($file, array_keys($files)) && !in_array($file, $parsed_files)) {
       
    93         $unchanged_resources[$key] = $file;
       
    94       }
       
    95     }
       
    96     module_implements('', FALSE, TRUE);
       
    97     _registry_check_code(REGISTRY_RESET_LOOKUP_CACHE);
       
    98   }
       
    99   catch (Exception $e) {
       
   100     $transaction->rollback();
       
   101     watchdog_exception('registry', $e);
       
   102     throw $e;
       
   103   }
       
   104 
       
   105   // We have some unchanged resources, warm up the cache - no need to pay
       
   106   // for looking them up again.
       
   107   if (count($unchanged_resources) > 0) {
       
   108     cache_set('lookup_cache', $unchanged_resources, 'cache_bootstrap');
       
   109   }
       
   110 }
       
   111 
       
   112 /**
       
   113  * Return the list of files in registry_file
       
   114  */
       
   115 function registry_get_parsed_files() {
       
   116   $files = array();
       
   117   // We want the result as a keyed array.
       
   118   $files = db_query("SELECT * FROM {registry_file}")->fetchAllAssoc('filename', PDO::FETCH_ASSOC);
       
   119   return $files;
       
   120 }
       
   121 
       
   122 /**
       
   123  * Parse all changed files and save their interface and class listings.
       
   124  *
       
   125  * Parse all files that have changed since the registry was last built, and save
       
   126  * their interface and class listings.
       
   127  *
       
   128  * @param $files
       
   129  *  The list of files to check and parse.
       
   130  */
       
   131 function _registry_parse_files($files) {
       
   132   $parsed_files = array();
       
   133   foreach ($files as $filename => $file) {
       
   134     if (file_exists($filename)) {
       
   135       $hash = hash_file('sha256', $filename);
       
   136       if (empty($file['hash']) || $file['hash'] != $hash) {
       
   137         $file['hash'] = $hash;
       
   138         $parsed_files[$filename] = $file;
       
   139       }
       
   140     }
       
   141   }
       
   142   foreach ($parsed_files as $filename => $file) {
       
   143     _registry_parse_file($filename, file_get_contents($filename), $file['module'], $file['weight']);
       
   144     db_merge('registry_file')
       
   145       ->key(array('filename' => $filename))
       
   146       ->fields(array(
       
   147         'hash' => $file['hash'],
       
   148       ))
       
   149       ->execute();
       
   150   }
       
   151   return array_keys($parsed_files);
       
   152 }
       
   153 
       
   154 /**
       
   155  * Parse a file and save its interface and class listings.
       
   156  *
       
   157  * @param $filename
       
   158  *   Name of the file we are going to parse.
       
   159  * @param $contents
       
   160  *   Contents of the file we are going to parse as a string.
       
   161  * @param $module
       
   162  *   (optional) Name of the module this file belongs to.
       
   163  * @param $weight
       
   164  *   (optional) Weight of the module.
       
   165  */
       
   166 function _registry_parse_file($filename, $contents, $module = '', $weight = 0) {
       
   167   if (preg_match_all('/^\s*(?:abstract|final)?\s*(class|interface|trait)\s+([a-zA-Z0-9_]+)/m', $contents, $matches)) {
       
   168     foreach ($matches[2] as $key => $name) {
       
   169       db_merge('registry')
       
   170         ->key(array(
       
   171           'name' => $name,
       
   172           'type' => $matches[1][$key],
       
   173         ))
       
   174         ->fields(array(
       
   175           'filename' => $filename,
       
   176           'module' => $module,
       
   177           'weight' => $weight,
       
   178         ))
       
   179         ->execute();
       
   180     }
       
   181     // Delete any resources for this file where the name is not in the list
       
   182     // we just merged in.
       
   183     db_delete('registry')
       
   184       ->condition('filename', $filename)
       
   185       ->condition('name', $matches[2], 'NOT IN')
       
   186       ->execute();
       
   187   }
       
   188 }
       
   189 
       
   190 /**
       
   191  * @} End of "defgroup registry".
       
   192  */