[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/sites/all/modules/features/ -> features.export.inc (source)

   1  <?php
   2  // $Id: features.export.inc,v 1.1.2.62 2010/08/11 19:49:05 yhahn Exp $
   3  
   4  /**
   5   * @param $items
   6   * @param $module_name
   7   * @return
   8   */
   9  function features_populate($items, $dependencies, $module_name) {
  10    // Sanitize items.
  11    $items = array_filter($items);
  12    $items['dependencies'] = drupal_map_assoc(array_filter($dependencies));
  13  
  14    // Populate stub
  15    $stub = array('features' => array(), 'dependencies' => array(), 'conflicts' => array());
  16    $export = _features_populate($items, $stub, $module_name);
  17  
  18    // Allow other modules to alter the export.
  19    drupal_alter('features_export', $export, $module_name);
  20  
  21    // Clean up and standardize order
  22    foreach (array_keys($export['features']) as $k) {
  23      ksort($export['features'][$k]);
  24    }
  25    ksort($export['features']);
  26    ksort($export['dependencies']);
  27  
  28    return $export;
  29  }
  30  
  31  /**
  32   * Iterate and descend into a feature definition to extract module
  33   * dependencies and feature definition. Calls hook_features_export for modules
  34   * that implement it.
  35   *
  36   * @param $pipe
  37   *  Associative of array of module => info-for-module
  38   * @param $export
  39   *  Associative array of items, and module dependencies which define a feature.
  40   *  Passed by reference.
  41   *
  42   * @return fully populated $export array.
  43   */
  44  function _features_populate($pipe, &$export, $module_name = '') {
  45    features_include();
  46    foreach ($pipe as $component => $data) {
  47      if (features_hook($component, 'features_export')) {
  48        // Pass module-specific data and export array.
  49        // We don't use features_invoke() here since we need to pass $export by reference.
  50        $function = "{$component}_features_export";
  51        $more = $function($data, $export, $module_name);
  52        // Allow other modules to manipulate the pipe to add in additional modules.
  53        drupal_alter('features_pipe_' . $component, $more, $data, $export, $module_name);
  54        // Allow for export functions to request additional exports.
  55        if (!empty($more)) {
  56          _features_populate($more, $export, $module_name);
  57        }
  58      }
  59    }
  60    return $export;
  61  }
  62  
  63  /**
  64   * Iterates over a list of dependencies and kills modules that are
  65   * captured by other modules 'higher up'.
  66   */
  67  function _features_export_minimize_dependencies($dependencies, $module_name = '') {
  68    // Ensure that the module doesn't depend upon itself
  69    if (!empty($module_name) && !empty($dependencies[$module_name])) {
  70      unset($dependencies[$module_name]);
  71    }
  72  
  73    // Do some cleanup:
  74    // - Remove modules required by Drupal core.
  75    // - Protect against direct circular dependencies.
  76    // - Remove "intermediate" dependencies.
  77    $required = drupal_required_modules();
  78    foreach ($dependencies as $k => $v) {
  79      if (empty($v) || in_array($v, $required)) {
  80        unset($dependencies[$k]);
  81      }
  82      else {
  83        $module = features_get_modules($v);
  84        if ($module && !empty($module->info['dependencies'])) {
  85          // If this dependency depends on the module itself, we have a circular dependency.
  86          // Don't let it happen. Only you can prevent forest fires.
  87          if (in_array($module_name, $module->info['dependencies'])) {
  88            unset($dependencies[$k]);
  89          }
  90          // Iterate through the dependency's dependencies and remove any dependencies
  91          // that are captured by it.
  92          else {
  93            foreach ($module->info['dependencies'] as $j => $dependency) {
  94              if (array_search($dependency, $dependencies) !== FALSE) {
  95                $position = array_search($dependency, $dependencies);
  96                unset($dependencies[$position]);
  97              }
  98            }
  99          }
 100        }
 101      }
 102    }
 103    return drupal_map_assoc(array_unique($dependencies));
 104  }
 105  
 106  /**
 107   * Iterates over a list of dependencies and maximize the list of modules.
 108   */
 109  function _features_export_maximize_dependencies($dependencies, $module_name = '', $first = TRUE) {
 110    $maximized = $dependencies;
 111    foreach ($dependencies as $k => $v) {
 112      $module = features_get_modules($v);
 113      if ($module && !empty($module->info['dependencies'])) {
 114        $maximized = array_merge($maximized, _features_export_maximize_dependencies($module->info['dependencies'], $module_name, FALSE));
 115      }
 116    }
 117    return array_unique($maximized);
 118  }
 119  
 120  /**
 121   * Prepare a feature export array into a finalized info array.
 122   */
 123  function features_export_prepare($export, $module_name, $reset = FALSE) {
 124    $existing = features_get_modules($module_name, $reset);
 125  
 126    // Prepare info string -- if module exists, merge into its existing info file
 127    $defaults = $existing ? $existing->info : array('core' => '6.x', 'package' => 'Features');
 128    $export = array_merge($defaults, $export);
 129  
 130    // Cleanup info array
 131    foreach ($export['features'] as $component => $data) {
 132      $export['features'][$component] = array_keys($data);
 133    }
 134    if (isset($export['dependencies'])) {
 135      $export['dependencies'] = array_values($export['dependencies']);
 136    }
 137    if (isset($export['conflicts'])) {
 138      unset($export['conflicts']);
 139    }
 140    ksort($export);
 141    return $export;
 142  }
 143  
 144  /**
 145   * Generate an array of hooks and their raw code.
 146   */
 147  function features_export_render_hooks($export, $module_name, $reset = FALSE) {
 148    features_include();
 149    $code = array();
 150  
 151    // Sort components to keep exported code consistent
 152    ksort($export['features']);
 153  
 154    foreach ($export['features'] as $component => $data) {
 155      if (!empty($data)) {
 156        // Sort the items so that we don't generate different exports based on order
 157        asort($data);
 158        if (features_hook($component, 'features_export_render')) {
 159          $hooks = features_invoke($component, 'features_export_render', $module_name, $data, $export);
 160          $code[$component] = $hooks;
 161        }
 162      }
 163    }
 164    return $code;
 165  }
 166  
 167  /**
 168   * Render feature export into an array representing its files.
 169   *
 170   * @param $export
 171   *  An exported feature definition.
 172   * @param $module_name
 173   *  The name of the module to be exported.
 174   * @param $reset
 175   *  Boolean flag for resetting the module cache. Only set to true when
 176   *  doing a final export for delivery.
 177   *
 178   * @return array of info file and module file contents.
 179   */
 180  function features_export_render($export, $module_name, $reset = FALSE) {
 181    $code = array();
 182  
 183    // Generate hook code
 184    $component_hooks = features_export_render_hooks($export, $module_name, $reset);
 185    $components = features_get_components();
 186  
 187    // Group component code into their respective files
 188    foreach ($component_hooks as $component => $hooks) {
 189      $file = array('name' => 'features');
 190      if (isset($components[$component]['default_file'])) {
 191        switch ($components[$component]['default_file']) {
 192          case FEATURES_DEFAULTS_INCLUDED:
 193            $file['name'] = "features.$component";
 194            break;
 195          case FEATURES_DEFAULTS_CUSTOM:
 196            $file['name'] = $components[$component]['default_filename'];
 197            break;
 198        }
 199      }
 200  
 201      if (!isset($code[$file['name']])) {
 202        $code[$file['name']] = array();
 203      }
 204  
 205      foreach ($hooks as $hook_name => $hook_code) {
 206        $code[$file['name']][$hook_name] = features_export_render_defaults($module_name, $hook_name, $hook_code);
 207      }
 208    }
 209  
 210    // Finalize strings to be written to files
 211    foreach ($code as $filename => $contents) {
 212      $code[$filename] = "<?php\n\n". implode("\n\n", $contents) ."\n";
 213    }
 214  
 215    // Generate info file output
 216    $export = features_export_prepare($export, $module_name, $reset);
 217    $code['info'] = features_export_info($export);
 218  
 219    // Prepare the module
 220    // If module exists, let it be and include it in the files
 221    if ($existing = features_get_modules($module_name, TRUE)) {
 222      $code['module'] = file_get_contents($existing->filename);
 223  
 224      // If the current module file does not reference the features.inc include,
 225      // set a warning message.
 226      if (isset($code['features']) && strpos($code['module'], "{$module_name}.features.inc") === FALSE) {
 227        features_log(t('@module does not appear to include the @include file.', array('@module' => "{$module_name}.module", '@include' => "{$module_name}.features.inc")), 'warning');
 228      }
 229  
 230      // Deprecated files. Display a message for any of these files letting the
 231      // user know that they may be removed.
 232      $deprecated = array(
 233        "{$module_name}.defaults",
 234        "{$module_name}.features.views",
 235        "{$module_name}.features.node"
 236      );
 237      foreach (file_scan_directory(drupal_get_path('module', $module_name), '.*') as $file) {
 238        if (in_array($file->name, $deprecated, TRUE)) {
 239          features_log(t('The file @filename has been deprecated and can be removed.', array('@filename' => $file->basename)), 'status');
 240        }
 241        elseif ($file->name === "{$module_name}.features" && empty($code['features'])) {
 242          $code['features'] = "<?php\n\n// This file is deprecated and can be removed.\n// Please remove include_once('{$module_name}.features.inc') in {$module_name}.module as well.\n";
 243        }
 244      }
 245    }
 246    // Add a stub module to include the defaults
 247    else if (!empty($code['features'])) {
 248      $code['module'] = "<?php\n\ninclude_once('{$module_name}.features.inc');\n";
 249    }
 250    else {
 251      $code['module'] = "<?php\n\n// Drupal needs this blank file.\n";
 252    }
 253    return $code;
 254  }
 255  
 256  /**
 257   * Detect differences between DB and code components of a feature.
 258   */
 259  function features_detect_overrides($module) {
 260    static $cache;
 261    if (!isset($cache)) {
 262      $cache = array();
 263    }
 264    if (!isset($cache[$module->name])) {
 265      // Rebuild feature from .info file description and prepare an export from current DB state.
 266      $export = features_populate($module->info['features'], $module->info['dependencies'], $module->name);
 267      $export = features_export_prepare($export, $module->name);
 268  
 269      $overridden = array();
 270  
 271      // Compare feature info
 272      _features_sanitize($module->info);
 273      _features_sanitize($export);
 274  
 275      $compare = array('normal' => features_export_info($export), 'default' => features_export_info($module->info));
 276      if ($compare['normal'] !== $compare['default']) {
 277        $overridden['info'] = $compare;
 278      }
 279  
 280      // Collect differences at a per-component level
 281      $states = features_get_component_states(array($module->name), FALSE);
 282      foreach ($states[$module->name] as $component => $state) {
 283        if ($state != FEATURES_DEFAULT) {
 284          $normal = features_get_normal($component, $module->name);
 285          $default = features_get_default($component, $module->name);
 286          _features_sanitize($normal);
 287          _features_sanitize($default);
 288  
 289          $compare = array('normal' => features_var_export($normal), 'default' => features_var_export($default));
 290          if (_features_linetrim($compare['normal']) !== _features_linetrim($compare['default'])) {
 291            $overridden[$component] = $compare;
 292          }
 293        }
 294      }
 295      $cache[$module->name] = $overridden;
 296    }
 297    return $cache[$module->name];
 298  }
 299  
 300  /**
 301   * Gets the available default hooks keyed by components.
 302   */
 303  function features_get_default_hooks($component = NULL, $reset = FALSE) {
 304    static $hooks;
 305    if (!isset($hooks) || $reset) {
 306      $hooks = array();
 307      features_include();
 308      foreach (module_implements('features_api') as $module) {
 309        $info = module_invoke($module, 'features_api');
 310        foreach ($info as $k => $v) {
 311          if (isset($v['default_hook'])) {
 312            $hooks[$k] = $v['default_hook'];
 313          }
 314        }
 315      }
 316    }
 317    if (isset($component)) {
 318      return isset($hooks[$component]) ? $hooks[$component] : FALSE;
 319    }
 320    return $hooks;
 321  }
 322  
 323  /**
 324   * Return a code string representing an implementation of a defaults module hook.
 325   */
 326  function features_export_render_defaults($module, $hook, $code) {
 327    $output = array();
 328    $output[] = "/**";
 329    $output[] = " * Implementation of hook_{$hook}().";
 330    $output[] = " */";
 331    $output[] = "function {$module}_{$hook}() {";
 332    $output[] = $code;
 333    $output[] = "}";
 334    return implode("\n", $output);
 335  }
 336  
 337  /**
 338   * Generate code friendly to the Drupal .info format from a structured array.
 339   *
 340   * @param $info
 341   *   An array or single value to put in a module's .info file.
 342   * @param $parents
 343   *   Array of parent keys (internal use only).
 344   *
 345   * @return
 346   *   A code string ready to be written to a module's .info file.
 347   */
 348  function features_export_info($info, $parents = array()) {
 349    $output = '';
 350    if (is_array($info)) {
 351      foreach ($info as $k => $v) {
 352        $child = $parents;
 353        $child[] = $k;
 354        $output .= features_export_info($v, $child);
 355      }
 356    }
 357    else if (!empty($info) && count($parents)) {
 358      $line = array_shift($parents);
 359      foreach ($parents as $key) {
 360        $line .= is_numeric($key) ? "[]" : "[{$key}]";
 361      }
 362      $line .=  " = \"{$info}\"\n";
 363      return $line;
 364    }
 365    return $output;
 366  }
 367  
 368  /**
 369   * Tar creation function. Written by dmitrig01.
 370   *
 371   * @param $name
 372   *   Filename of the file to be tarred.
 373   * @param $contents
 374   *   String contents of the file.
 375   *
 376   * @return
 377   *   A string of the tar file contents.
 378   */
 379  function features_tar_create($name, $contents) {
 380    $tar = '';
 381    $binary_data_first = pack("a100a8a8a8a12A12",
 382      $name,
 383      '100644 ', // File permissions
 384      '   765 ', // UID,
 385      '   765 ', // GID,
 386      sprintf("%11s ", decoct(strlen($contents))), // Filesize,
 387      sprintf("%11s", decoct(time())) // Creation time
 388    );
 389    $binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", '', '', '', '', '', '', '', '', '', '');
 390    
 391    $checksum = 0;
 392    for ($i = 0; $i < 148; $i++) {
 393      $checksum += ord(substr($binary_data_first, $i, 1));
 394    }
 395    for ($i = 148; $i < 156; $i++) {
 396      $checksum += ord(' ');
 397    }
 398    for ($i = 156, $j = 0; $i < 512; $i++, $j++) {
 399      $checksum += ord(substr($binary_data_last, $j, 1));
 400    }
 401    
 402    $tar .= $binary_data_first;
 403    $tar .= pack("a8", sprintf("%6s ", decoct($checksum)));
 404    $tar .= $binary_data_last;
 405    
 406    $buffer = str_split($contents, 512);
 407    foreach ($buffer as $item) {
 408      $tar .= pack("a512", $item);
 409    }
 410    return $tar;
 411  }
 412  
 413  /**
 414   * Export var function -- from Views.
 415   */
 416  function features_var_export($var, $prefix = '', $init = TRUE) {
 417    if (is_object($var)) {
 418      $output = method_exists($var, 'export') ? $var->export() : features_var_export((array) $var);
 419    }
 420    else if (is_array($var)) {
 421      if (empty($var)) {
 422        $output = 'array()';
 423      }
 424      else {
 425        $output = "array(\n";
 426        foreach ($var as $key => $value) {
 427          $output .= "  '$key' => " . features_var_export($value, '  ', FALSE) . ",\n";
 428        }
 429        $output .= ')';
 430      }
 431    }
 432    else if (is_bool($var)) {
 433      $output = $var ? 'TRUE' : 'FALSE';
 434    }
 435    else if (is_string($var) && strpos($var, "\n") !== FALSE) {
 436      // Replace line breaks in strings with a token for replacement
 437      // at the very end. This protects whitespace in strings from
 438      // unintentional indentation.
 439      $var = str_replace("\n", "***BREAK***", $var);
 440      $output = var_export($var, TRUE);
 441    }
 442    else {
 443      $output = var_export($var, TRUE);
 444    }
 445  
 446    if ($prefix) {
 447      $output = str_replace("\n", "\n$prefix", $output);
 448    }
 449  
 450    if ($init) {
 451      $output = str_replace("***BREAK***", "\n", $output);
 452    }
 453  
 454    return $output;
 455  }
 456  
 457  /**
 458   * Helper function to return an array of t()'d translatables strings.
 459   * Useful for providing a separate array of translatables with your
 460   * export so that string extractors like potx can detect them.
 461   */
 462  function features_translatables_export($translatables, $indent = '') {
 463    $output = '';
 464    $translatables = array_filter(array_unique($translatables));
 465    if (!empty($translatables)) {
 466      $output .= "{$indent}// Translatables\n";
 467      $output .= "{$indent}// Included for use with string extractors like potx.\n";
 468      sort($translatables);
 469      foreach ($translatables as $string) {
 470        $output .= "{$indent}t(" . features_var_export($string) . ");\n";
 471      }
 472    }
 473    return $output;
 474  }
 475  
 476  /**
 477   * Get a summary storage state for a feature.
 478   */
 479  function features_get_storage($module_name) {
 480    // Get component states, and array_diff against array(FEATURES_DEFAULT).
 481    // If the returned array has any states that don't match FEATURES_DEFAULT,
 482    // return the highest state.
 483    $states = features_get_component_states(array($module_name), FALSE);
 484    $states = array_diff($states[$module_name], array(FEATURES_DEFAULT));
 485    $storage = !empty($states) ? max($states) : FEATURES_DEFAULT;
 486    return $storage;
 487  }
 488  
 489  /**
 490   * Wrapper around features_get_[storage] to return an md5hash of a normalized
 491   * defaults/normal object array. Can be used to compare normal/default states
 492   * of a module's component.
 493   */
 494  function features_get_signature($state = 'default', $module_name, $component, $reset = FALSE) {
 495    switch ($state) {
 496      case 'cache':
 497        $codecache = variable_get('features_codecache', array());
 498        return isset($codecache[$module_name][$component]) ? $codecache[$module_name][$component] : FALSE;
 499      case 'default':
 500        $objects = features_get_default($component, $module_name, TRUE, $reset);
 501        break;
 502      case 'normal':
 503        $objects = features_get_normal($component, $module_name, $reset);
 504        break;
 505    }
 506    if (!empty($objects)) {
 507      $objects = (array) $objects;
 508      _features_sanitize($objects);
 509      return md5(_features_linetrim(features_var_export($objects)));
 510    }
 511    return FALSE;
 512  }
 513  
 514  /**
 515   * Set the signature of a module/component pair in the codecache.
 516   */
 517  function features_set_signature($module, $component, $signature = NULL) {
 518    $var_codecache = variable_get('features_codecache', array());
 519    $signature = isset($signature) ? $signature : features_get_signature('default', $module, $component, TRUE);
 520    $var_codecache[$module][$component] = $signature;
 521    variable_set('features_codecache', $var_codecache);
 522  }
 523  
 524  /**
 525   * Processing semaphore operations.
 526   */
 527  function features_semaphore($op, $component) {
 528    // Note: we don't use variable_get() here as the inited variable
 529    // static cache may be stale. Retrieving directly from the DB narrows
 530    // the possibility of collision.
 531    $semaphore = db_result(db_query("SELECT value FROM {variable} WHERE name = 'features_semaphore'"));
 532    $semaphore = !empty($semaphore) ? unserialize($semaphore) : array();
 533  
 534    switch ($op) {
 535      case 'get':
 536        return isset($semaphore[$component]) ? $semaphore[$component] : FALSE;
 537      case 'set':
 538        $semaphore[$component] = time();
 539        variable_set('features_semaphore', $semaphore);
 540        break;
 541      case 'del':
 542        if (isset($semaphore[$component])) {
 543          unset($semaphore[$component]);
 544          variable_set('features_semaphore', $semaphore);
 545        }
 546        break;
 547    }
 548  }
 549  
 550  /**
 551   * Get normal objects for a given module/component pair.
 552   */
 553  function features_get_normal($component, $module_name, $reset = FALSE) {
 554    static $cache;
 555    if (!isset($cache) || $reset) {
 556      $cache = array();
 557    }
 558    if (!isset($cache[$module_name][$component])) {
 559      features_include();
 560      $code = NULL;
 561      $module = features_get_features($module_name);
 562  
 563      // Special handling for dependencies component.
 564      if ($component === 'dependencies') {
 565        $cache[$module_name][$component] = isset($module->info['dependencies']) ? array_filter($module->info['dependencies'], 'module_exists') : array();
 566      }
 567      // All other components.
 568      else {
 569        $default_hook = features_get_default_hooks($component);
 570        if ($module && $default_hook && isset($module->info['features'][$component]) && features_hook($component, 'features_export_render')) {
 571          $code = features_invoke($component, 'features_export_render', $module_name, $module->info['features'][$component]);
 572          $cache[$module_name][$component] = isset($code[$default_hook]) ? eval($code[$default_hook]) : FALSE;
 573        }
 574      }
 575  
 576      // Clear out vars for memory's sake.
 577      unset($code);
 578      unset($module);
 579    }
 580    return isset($cache[$module_name][$component]) ? $cache[$module_name][$component] : FALSE;
 581  }
 582  
 583  /**
 584   * Get defaults for a given module/component pair.
 585   */
 586  function features_get_default($component, $module_name = NULL, $alter = TRUE, $reset = FALSE) {
 587    static $cache = array();
 588    features_include();
 589    features_include_defaults($component);
 590    $default_hook = features_get_default_hooks($component);
 591    $components = features_get_components();
 592  
 593    // Collect defaults for all modules if no module name was specified.
 594    if (isset($module_name)) {
 595      $modules = array($module_name);
 596    }
 597    else {
 598      $modules = ($component === 'dependencies') ? array_keys(features_get_features()) : module_implements($default_hook);
 599    }
 600  
 601    // Collect and cache information for each specified module.
 602    foreach ($modules as $m) {
 603      if (!isset($cache[$component][$m]) || $reset) {
 604        // Special handling for dependencies component.
 605        if ($component === 'dependencies') {
 606          $module = features_get_features($m);
 607          $cache[$component][$m] = isset($module->info['dependencies']) ? $module->info['dependencies'] : array();
 608          unset($module);
 609        }
 610        // All other components
 611        else {
 612          if ($default_hook && module_hook($m, $default_hook)) {
 613            $cache[$component][$m] = module_invoke($m, $default_hook);
 614            if ($alter) {
 615              drupal_alter($default_hook, $cache[$component][$m]);
 616            }
 617          }
 618          else {
 619            $cache[$component][$m] = FALSE;
 620          }
 621        }
 622      }
 623    }
 624  
 625    // A specific module was specified. Retrieve only its components.
 626    if (isset($module_name)) {
 627      return isset($cache[$component][$module_name]) ? $cache[$component][$module_name] : FALSE;
 628    }
 629    // No module was specified. Retrieve all components.
 630    $all_defaults = array();
 631    if (isset($cache[$component])) {
 632      foreach (array_filter($cache[$component]) as $module_components) {
 633        $all_defaults = array_merge($all_defaults, $module_components);
 634      }
 635    }
 636    return $all_defaults;
 637  }
 638  
 639  /**
 640   * Get a map of components to their providing modules.
 641   */
 642  function features_get_default_map($component, $attribute = NULL, $callback = NULL, $reset = FALSE) {
 643    static $map = array();
 644    features_include();
 645    features_include_defaults($component);
 646    if ((!isset($map[$component]) || $reset) && $default_hook = features_get_default_hooks($component)) {
 647      $map[$component] = array();
 648      foreach (module_implements($default_hook) as $module) {
 649        if ($defaults = features_get_default($component, $module)) {
 650          foreach ($defaults as $key => $object) {
 651            if (isset($callback)) {
 652              if ($object_key = $callback($object)) {
 653                $map[$component][$object_key] = $module;
 654              }
 655            }
 656            elseif (isset($attribute)) {
 657              if (is_object($object) && isset($object->{$attribute})) {
 658                $map[$component][$object->{$attribute}] = $module;
 659              }
 660              elseif (is_array($object) && isset($object[$attribute])) {
 661                $map[$component][$object[$attribute]] = $module;
 662              }
 663            }
 664            elseif (!isset($attribute) && !isset($callback)) {
 665              if (!is_numeric($key)) {
 666                $map[$component][$key] = $module;
 667              }
 668            }
 669            else {
 670              return FALSE;
 671            }
 672          }
 673        }
 674      }
 675    }
 676    return isset($map[$component]) ? $map[$component] : FALSE;
 677  }
 678  
 679  /**
 680   * Retrieve an array of features/components and their current states.
 681   */
 682  function features_get_component_states($features = array(), $rebuild_only = TRUE, $reset = FALSE) {
 683    static $cache;
 684    if (!isset($cache) || $reset) {
 685      $cache = array();
 686    }
 687  
 688    $features = !empty($features) ? $features : array_keys(features_get_features());
 689  
 690    // Retrieve only rebuildable components if requested.
 691    features_include();
 692    $components = array_keys(features_get_components());
 693    if ($rebuild_only) {
 694      foreach ($components as $k => $component) {
 695        if (!features_hook($component, 'features_rebuild')) {
 696          unset($components[$k]);
 697        }
 698      }
 699    }
 700  
 701    foreach ($features as $feature) {
 702      $cache[$feature] = isset($cache[$feature]) ? $cache[$feature] : array();
 703      if (module_exists($feature)) {
 704        foreach ($components as $component) {
 705          if (!isset($cache[$feature][$component])) {
 706            $normal = features_get_signature('normal', $feature, $component, $reset);
 707            $default = features_get_signature('default', $feature, $component, $reset);
 708            $codecache = features_get_signature('cache', $feature, $component, $reset);
 709            $semaphore = features_semaphore('get', $component);
 710  
 711            // DB and code states match, there is nothing more to check.
 712            if ($normal == $default) {
 713              $cache[$feature][$component] = FEATURES_DEFAULT;
 714  
 715              // Stale semaphores can be deleted.
 716              features_semaphore('del', $component);
 717  
 718              // Update code cache if it is stale, clear out semaphore if it stale.
 719              if ($default != $codecache) {
 720                features_set_signature($feature, $component, $default);
 721              }
 722            }
 723            // Component properly implements exportables.
 724            else if (!features_hook($component, 'features_rebuild')) {
 725              $cache[$feature][$component] = FEATURES_OVERRIDDEN;
 726            }
 727            // Component does not implement exportables.
 728            else {
 729              if (empty($semaphore)) {
 730                // Exception for dependencies. Dependencies are always rebuildable.
 731                if ($component === 'dependencies') {
 732                  $cache[$feature][$component] = FEATURES_REBUILDABLE;
 733                }
 734                // All other rebuildable components require comparison.
 735                else {
 736                  // Code has not changed, but DB does not match. User has DB overrides.
 737                  if ($codecache == $default) {
 738                    $cache[$feature][$component] = FEATURES_OVERRIDDEN;
 739                  }
 740                  // DB has no modifications to prior code state (or this is initial install).
 741                  else if ($codecache == $normal || empty($codecache)) {
 742                    $cache[$feature][$component] = FEATURES_REBUILDABLE;
 743                  }
 744                  // None of the states match. Requires user intervention.
 745                  else if ($codecache != $default) {
 746                    $cache[$feature][$component] = FEATURES_NEEDS_REVIEW;
 747                  }
 748                }
 749              }
 750              else {
 751                // Semaphore is still within processing horizon. Do nothing.
 752                if ((time() - $semaphore) < FEATURES_SEMAPHORE_TIMEOUT) {
 753                  $cache[$feature][$component] = FEATURES_REBUILDING;
 754                }
 755                // A stale semaphore means a previous rebuild attempt did not complete.
 756                // Attempt to complete the rebuild.
 757                else {
 758                  $cache[$feature][$component] = FEATURES_REBUILDABLE;
 759                }
 760              }
 761            }
 762          }
 763        }
 764      }
 765    }
 766  
 767    // Filter cached components on the way out to ensure that even if we have
 768    // cached more data than has been requested, the return value only reflects
 769    // the requested features/components.
 770    $return = $cache;
 771    $return = array_intersect_key($return, array_flip($features));
 772    foreach ($return as $k => $v) {
 773      $return[$k] = array_intersect_key($return[$k], array_flip($components));
 774    }
 775    return $return;
 776  }
 777  
 778  /**
 779   * Helper function to eliminate whitespace differences in code.
 780   */
 781  function _features_linetrim($code) {
 782    $code = explode("\n", $code);
 783    foreach ($code as $k => $line) {
 784      $code[$k] = trim($line);
 785    }
 786    return implode("\n", $code);
 787  }
 788  
 789  /**
 790   * "Sanitizes" an array recursively, performing two key operations:
 791   * - Sort an array by its keys (assoc) or values (non-assoc)
 792   * - Remove any null or empty values for associative arrays (array_filter()).
 793   */
 794  function _features_sanitize(&$array) {
 795    if (is_array($array)) {
 796      $is_assoc = (array_keys($array) != range(0, count($array) - 1));
 797      if ($is_assoc) {
 798        ksort($array);
 799        $array = array_filter($array);
 800      }
 801      else {
 802        sort($array);
 803      }
 804      foreach ($array as $k => $v) {
 805        if (is_array($v)) {
 806          _features_sanitize($array[$k]);
 807        }
 808      }
 809    }
 810  }


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7