danielebarchiesi@4: array(), 'dependencies' => array(), 'conflicts' => array()) + $info + array('features_exclude' => array()); danielebarchiesi@4: $export = _features_populate($items, $stub, $module_name, TRUE); danielebarchiesi@4: danielebarchiesi@4: // Add Features API version. Any module with this entry in the .info file danielebarchiesi@4: // will be treated as a Feature and included in the admin/build/features UI. danielebarchiesi@4: $export['features']['features_api']['api:' . FEATURES_API] = TRUE; danielebarchiesi@4: // Allow other modules to alter the export. danielebarchiesi@4: drupal_alter('features_export', $export, $module_name); danielebarchiesi@4: danielebarchiesi@4: // Clean up and standardize order danielebarchiesi@4: foreach (array_keys($export['features']) as $k) { danielebarchiesi@4: ksort($export['features'][$k]); danielebarchiesi@4: } danielebarchiesi@4: ksort($export['features']); danielebarchiesi@4: ksort($export['dependencies']); danielebarchiesi@4: ksort($export['features_exclude']); danielebarchiesi@4: danielebarchiesi@4: return $export; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Iterate and descend into a feature definition to extract module danielebarchiesi@4: * dependencies and feature definition. Calls hook_features_export for modules danielebarchiesi@4: * that implement it. danielebarchiesi@4: * danielebarchiesi@4: * @param $pipe danielebarchiesi@4: * Associative of array of module => info-for-module danielebarchiesi@4: * @param $export danielebarchiesi@4: * Associative array of items, and module dependencies which define a feature. danielebarchiesi@4: * Passed by reference. danielebarchiesi@4: * danielebarchiesi@4: * @return fully populated $export array. danielebarchiesi@4: */ danielebarchiesi@4: function _features_populate($pipe, &$export, $module_name = '', $reset = FALSE) { danielebarchiesi@4: static $processed = array(); danielebarchiesi@4: features_include(); danielebarchiesi@4: if ($reset) { danielebarchiesi@4: $processed = array(); danielebarchiesi@4: } danielebarchiesi@4: foreach ($pipe as $component => $data) { danielebarchiesi@4: // Convert already defined items to dependencies. danielebarchiesi@4: // _features_resolve_dependencies($data, $export, $module_name, $component); danielebarchiesi@4: // Remove any excluded items. danielebarchiesi@4: if (!empty($export['features_exclude'][$component])) { danielebarchiesi@4: $data = array_diff($data, $export['features_exclude'][$component]); danielebarchiesi@4: if ($component == 'dependencies' && !empty($export['dependencies'])) { danielebarchiesi@4: $export['dependencies'] = array_diff($export['dependencies'], $export['features_exclude'][$component]); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: if (!empty($data) && $function = features_hook($component, 'features_export')) { danielebarchiesi@4: // Pass module-specific data and export array. danielebarchiesi@4: // We don't use features_invoke() here since we need to pass $export by reference. danielebarchiesi@4: $more = $function($data, $export, $module_name, $component); danielebarchiesi@4: // Add the context information. danielebarchiesi@4: $export['component'] = $component; danielebarchiesi@4: $export['module_name'] = $module_name; danielebarchiesi@4: // Allow other modules to manipulate the pipe to add in additional modules. danielebarchiesi@4: drupal_alter(array('features_pipe', 'features_pipe_' . $component), $more, $data, $export); danielebarchiesi@4: // Remove the component information. danielebarchiesi@4: unset($export['component']); danielebarchiesi@4: unset($export['module_name']); danielebarchiesi@4: // Allow for export functions to request additional exports, but avoid danielebarchiesi@4: // circular references on already processed components. danielebarchiesi@4: $processed[$component] = isset($processed[$component]) ? array_merge($processed[$component], $data) : $data; danielebarchiesi@4: danielebarchiesi@4: if (!empty($more)) { danielebarchiesi@4: // Remove already processed components. danielebarchiesi@4: foreach ($more as $component_name => $component_data) { danielebarchiesi@4: if (isset($processed[$component_name])) { danielebarchiesi@4: $more[$component_name] = array_diff($component_data, $processed[$component_name]); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: if ($more = array_filter($more)) { danielebarchiesi@4: _features_populate($more, $export, $module_name); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return $export; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Iterates over data and convert to dependencies if already defined elsewhere. danielebarchiesi@4: */ danielebarchiesi@4: function _features_resolve_dependencies(&$data, &$export, $module_name, $component) { danielebarchiesi@4: if ($map = features_get_default_map($component)) { danielebarchiesi@4: foreach ($data as $key => $item) { danielebarchiesi@4: // If this node type is provided by a different module, add it as a dependency danielebarchiesi@4: if (isset($map[$item]) && $map[$item] != $module_name) { danielebarchiesi@4: $export['dependencies'][$map[$item]] = $map[$item]; danielebarchiesi@4: unset($data[$key]); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Iterates over a list of dependencies and kills modules that are danielebarchiesi@4: * captured by other modules 'higher up'. danielebarchiesi@4: */ danielebarchiesi@4: function _features_export_minimize_dependencies($dependencies, $module_name = '') { danielebarchiesi@4: // Ensure that the module doesn't depend upon itself danielebarchiesi@4: if (!empty($module_name) && !empty($dependencies[$module_name])) { danielebarchiesi@4: unset($dependencies[$module_name]); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // Do some cleanup: danielebarchiesi@4: // - Remove modules required by Drupal core. danielebarchiesi@4: // - Protect against direct circular dependencies. danielebarchiesi@4: // - Remove "intermediate" dependencies. danielebarchiesi@4: $required = drupal_required_modules(); danielebarchiesi@4: foreach ($dependencies as $k => $v) { danielebarchiesi@4: if (empty($v) || in_array($v, $required)) { danielebarchiesi@4: unset($dependencies[$k]); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $module = features_get_modules($v); danielebarchiesi@4: if ($module && !empty($module->info['dependencies'])) { danielebarchiesi@4: // If this dependency depends on the module itself, we have a circular dependency. danielebarchiesi@4: // Don't let it happen. Only you can prevent forest fires. danielebarchiesi@4: if (in_array($module_name, $module->info['dependencies'])) { danielebarchiesi@4: unset($dependencies[$k]); danielebarchiesi@4: } danielebarchiesi@4: // Iterate through the dependency's dependencies and remove any dependencies danielebarchiesi@4: // that are captured by it. danielebarchiesi@4: else { danielebarchiesi@4: foreach ($module->info['dependencies'] as $j => $dependency) { danielebarchiesi@4: if (array_search($dependency, $dependencies) !== FALSE) { danielebarchiesi@4: $position = array_search($dependency, $dependencies); danielebarchiesi@4: unset($dependencies[$position]); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return drupal_map_assoc(array_unique($dependencies)); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Iterates over a list of dependencies and maximize the list of modules. danielebarchiesi@4: */ danielebarchiesi@4: function _features_export_maximize_dependencies($dependencies, $module_name = '', $maximized = array(), $first = TRUE) { danielebarchiesi@4: foreach ($dependencies as $k => $v) { danielebarchiesi@4: $parsed_dependency = drupal_parse_dependency($v); danielebarchiesi@4: $name = $parsed_dependency['name']; danielebarchiesi@4: if (!in_array($name, $maximized)) { danielebarchiesi@4: $maximized[] = $name; danielebarchiesi@4: $module = features_get_modules($name); danielebarchiesi@4: if ($module && !empty($module->info['dependencies'])) { danielebarchiesi@4: $maximized = array_merge($maximized, _features_export_maximize_dependencies($module->info['dependencies'], $module_name, $maximized, FALSE)); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return array_unique($maximized); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Prepare a feature export array into a finalized info array. danielebarchiesi@4: * @param $export danielebarchiesi@4: * An exported feature definition. danielebarchiesi@4: * @param $module_name danielebarchiesi@4: * The name of the module to be exported. danielebarchiesi@4: * @param $reset danielebarchiesi@4: * Boolean flag for resetting the module cache. Only set to true when danielebarchiesi@4: * doing a final export for delivery. danielebarchiesi@4: */ danielebarchiesi@4: function features_export_prepare($export, $module_name, $reset = FALSE, $add_deprecated = TRUE) { danielebarchiesi@4: $existing = features_get_modules($module_name, $reset); danielebarchiesi@4: danielebarchiesi@4: // copy certain exports directly into info danielebarchiesi@4: $copy_list = array('scripts', 'stylesheets'); danielebarchiesi@4: foreach ($copy_list as $item) { danielebarchiesi@4: if(isset($export[$item])) { danielebarchiesi@4: $existing->info[$item] = $export[$item]; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // Prepare info string -- if module exists, merge into its existing info file danielebarchiesi@4: $defaults = !empty($existing->info) ? $existing->info : array('core' => '7.x', 'package' => 'Features'); danielebarchiesi@4: $export = array_merge($defaults, $export); danielebarchiesi@4: danielebarchiesi@4: $deprecated = features_get_deprecated(); danielebarchiesi@4: // Cleanup info array danielebarchiesi@4: foreach ($export['features'] as $component => $data) { danielebarchiesi@4: // if performing the final export, do not export deprecated components danielebarchiesi@4: if (($reset || !$add_deprecated) && !empty($deprecated[$component])) { danielebarchiesi@4: unset($export['features'][$component]); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $export['features'][$component] = array_keys($data); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: if (isset($export['dependencies'])) { danielebarchiesi@4: $export['dependencies'] = array_values($export['dependencies']); danielebarchiesi@4: } danielebarchiesi@4: if (isset($export['conflicts'])) { danielebarchiesi@4: unset($export['conflicts']); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // Order info array. danielebarchiesi@4: $standard_info = array(); danielebarchiesi@4: foreach (array_merge(array('name', 'description', 'core', 'package', 'version', 'project', 'dependencies'), $copy_list) as $item) { danielebarchiesi@4: if (isset($export[$item])) { danielebarchiesi@4: $standard_info[$item] = $export[$item]; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: if (isset($export['php']) && ($export['php'] != DRUPAL_MINIMUM_PHP)) { danielebarchiesi@4: $standard_info['php'] = $export['php']; danielebarchiesi@4: } danielebarchiesi@4: unset($export['php']); danielebarchiesi@4: danielebarchiesi@4: $export = features_array_diff_assoc_recursive($export, $standard_info); danielebarchiesi@4: ksort($export); danielebarchiesi@4: return array_merge($standard_info, $export); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Generate an array of hooks and their raw code. danielebarchiesi@4: */ danielebarchiesi@4: function features_export_render_hooks($export, $module_name, $reset = FALSE) { danielebarchiesi@4: features_include(); danielebarchiesi@4: $code = array(); danielebarchiesi@4: danielebarchiesi@4: // Sort components to keep exported code consistent danielebarchiesi@4: ksort($export['features']); danielebarchiesi@4: danielebarchiesi@4: foreach ($export['features'] as $component => $data) { danielebarchiesi@4: if (!empty($data)) { danielebarchiesi@4: // Sort the items so that we don't generate different exports based on order danielebarchiesi@4: asort($data); danielebarchiesi@4: if (features_hook($component, 'features_export_render')) { danielebarchiesi@4: $hooks = features_invoke($component, 'features_export_render', $module_name, $data, $export); danielebarchiesi@4: $code[$component] = $hooks; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return $code; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Render feature export into an array representing its files. danielebarchiesi@4: * danielebarchiesi@4: * @param $export danielebarchiesi@4: * An exported feature definition. danielebarchiesi@4: * @param $module_name danielebarchiesi@4: * The name of the module to be exported. danielebarchiesi@4: * @param $reset danielebarchiesi@4: * Boolean flag for resetting the module cache. Only set to true when danielebarchiesi@4: * doing a final export for delivery. danielebarchiesi@4: * danielebarchiesi@4: * @return array of info file and module file contents. danielebarchiesi@4: */ danielebarchiesi@4: function features_export_render($export, $module_name, $reset = FALSE) { danielebarchiesi@4: $code = array(); danielebarchiesi@4: danielebarchiesi@4: // Generate hook code danielebarchiesi@4: $component_hooks = features_export_render_hooks($export, $module_name, $reset); danielebarchiesi@4: $components = features_get_components(); danielebarchiesi@4: $deprecated = features_get_deprecated($components); danielebarchiesi@4: danielebarchiesi@4: // Group component code into their respective files danielebarchiesi@4: foreach ($component_hooks as $component => $hooks) { danielebarchiesi@4: if ($reset && !empty($deprecated[$component])) { danielebarchiesi@4: // skip deprecated components on final export danielebarchiesi@4: continue; danielebarchiesi@4: } danielebarchiesi@4: $file = array('name' => 'features'); danielebarchiesi@4: if (isset($components[$component]['default_file'])) { danielebarchiesi@4: switch ($components[$component]['default_file']) { danielebarchiesi@4: case FEATURES_DEFAULTS_INCLUDED: danielebarchiesi@4: $file['name'] = "features.$component"; danielebarchiesi@4: break; danielebarchiesi@4: case FEATURES_DEFAULTS_CUSTOM: danielebarchiesi@4: $file['name'] = $components[$component]['default_filename']; danielebarchiesi@4: break; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: if (!isset($code[$file['name']])) { danielebarchiesi@4: $code[$file['name']] = array(); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: foreach ($hooks as $hook_name => $hook_info) { danielebarchiesi@4: $hook_code = is_array($hook_info) ? $hook_info['code'] : $hook_info; danielebarchiesi@4: $hook_args = is_array($hook_info) && !empty($hook_info['args']) ? $hook_info['args'] : ''; danielebarchiesi@4: $hook_file = is_array($hook_info) && !empty($hook_info['file']) ? $hook_info['file'] : $file['name']; danielebarchiesi@4: $code[$hook_file][$hook_name] = features_export_render_defaults($module_name, $hook_name, $hook_code, $hook_args); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // Finalize strings to be written to files danielebarchiesi@4: $code = array_filter($code); danielebarchiesi@4: foreach ($code as $filename => $contents) { danielebarchiesi@4: $code[$filename] = "filename); danielebarchiesi@4: danielebarchiesi@4: // If the current module file does not reference the features.inc include, danielebarchiesi@4: // @TODO this way of checking does not account for the possibility of inclusion instruction being commented out. danielebarchiesi@4: if (isset($code['features']) && strpos($code['module'], "{$module_name}.features.inc") === FALSE) { danielebarchiesi@4: // If .module does not begin with "{$module_name}.module", '@include' => "{$module_name}.features.inc")), 'warning'); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: // Remove the old message if it exists, else just remove the name, $deprecated_files, TRUE)) { danielebarchiesi@4: features_log(t('The file @filename has been deprecated and can be removed.', array('@filename' => $file->filename)), 'status'); danielebarchiesi@4: } danielebarchiesi@4: elseif ($file->name === "{$module_name}.features" && empty($code['features'])) { danielebarchiesi@4: // Try and remove features.inc include. danielebarchiesi@4: if (strpos($code['module'], "{$module_name}.features.inc")) { danielebarchiesi@4: $code['module'] = str_replace($modulefile_features_inc, $modulefile_blank, $code['module']); danielebarchiesi@4: } danielebarchiesi@4: // If unable to remove the include, add a message to remove. danielebarchiesi@4: if (strpos($code['module'], "{$module_name}.features.inc")) { danielebarchiesi@4: $code['features'] = "name])) { danielebarchiesi@4: // Rebuild feature from .info file description and prepare an export from current DB state. danielebarchiesi@4: $export = features_populate($module->info, $module->name); danielebarchiesi@4: $export = features_export_prepare($export, $module->name, FALSE, FALSE); danielebarchiesi@4: danielebarchiesi@4: $overridden = array(); danielebarchiesi@4: danielebarchiesi@4: // Compare feature info danielebarchiesi@4: _features_sanitize($module->info); danielebarchiesi@4: _features_sanitize($export); danielebarchiesi@4: danielebarchiesi@4: $compare = array('normal' => features_export_info($export), 'default' => features_export_info($module->info)); danielebarchiesi@4: if ($compare['normal'] !== $compare['default']) { danielebarchiesi@4: $overridden['info'] = $compare; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // Collect differences at a per-component level danielebarchiesi@4: $states = features_get_component_states(array($module->name), FALSE); danielebarchiesi@4: foreach ($states[$module->name] as $component => $state) { danielebarchiesi@4: if ($state != FEATURES_DEFAULT) { danielebarchiesi@4: $normal = features_get_normal($component, $module->name); danielebarchiesi@4: $default = features_get_default($component, $module->name); danielebarchiesi@4: _features_sanitize($normal); danielebarchiesi@4: _features_sanitize($default); danielebarchiesi@4: danielebarchiesi@4: $compare = array('normal' => features_var_export($normal), 'default' => features_var_export($default)); danielebarchiesi@4: if (_features_linetrim($compare['normal']) !== _features_linetrim($compare['default'])) { danielebarchiesi@4: $overridden[$component] = $compare; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: $cache[$module->name] = $overridden; danielebarchiesi@4: } danielebarchiesi@4: return $cache[$module->name]; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Gets the available default hooks keyed by components. danielebarchiesi@4: */ danielebarchiesi@4: function features_get_default_hooks($component = NULL, $reset = FALSE) { danielebarchiesi@4: return features_get_components($component, 'default_hook', $reset); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Gets the available default hooks keyed by components. danielebarchiesi@4: */ danielebarchiesi@4: function features_get_default_alter_hook($component) { danielebarchiesi@4: $default_hook = features_get_components($component, 'default_hook'); danielebarchiesi@4: $alter_hook = features_get_components($component, 'alter_hook'); danielebarchiesi@4: $alter_type = features_get_components($component, 'alter_type'); danielebarchiesi@4: return empty($alter_type) || $alter_type != 'none' ? ($alter_hook ? $alter_hook : $default_hook) : FALSE; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Return a code string representing an implementation of a defaults module hook. danielebarchiesi@4: */ danielebarchiesi@4: function features_export_render_defaults($module, $hook, $code, $args = '') { danielebarchiesi@4: $output = array(); danielebarchiesi@4: $output[] = "/**"; danielebarchiesi@4: $output[] = " * Implements hook_{$hook}()."; danielebarchiesi@4: $output[] = " */"; danielebarchiesi@4: $output[] = "function {$module}_{$hook}(" . $args . ") {"; danielebarchiesi@4: $output[] = $code; danielebarchiesi@4: $output[] = "}"; danielebarchiesi@4: return implode("\n", $output); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Generate code friendly to the Drupal .info format from a structured array. danielebarchiesi@4: * danielebarchiesi@4: * @param $info danielebarchiesi@4: * An array or single value to put in a module's .info file. danielebarchiesi@4: * @param $parents danielebarchiesi@4: * Array of parent keys (internal use only). danielebarchiesi@4: * danielebarchiesi@4: * @return danielebarchiesi@4: * A code string ready to be written to a module's .info file. danielebarchiesi@4: */ danielebarchiesi@4: function features_export_info($info, $parents = array()) { danielebarchiesi@4: $output = ''; danielebarchiesi@4: if (is_array($info)) { danielebarchiesi@4: foreach ($info as $k => $v) { danielebarchiesi@4: $child = $parents; danielebarchiesi@4: $child[] = $k; danielebarchiesi@4: $output .= features_export_info($v, $child); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: else if (!empty($info) && count($parents)) { danielebarchiesi@4: $line = array_shift($parents); danielebarchiesi@4: foreach ($parents as $key) { danielebarchiesi@4: $line .= is_numeric($key) ? "[]" : "[{$key}]"; danielebarchiesi@4: } danielebarchiesi@4: $line .= " = {$info}\n"; danielebarchiesi@4: return $line; danielebarchiesi@4: } danielebarchiesi@4: return $output; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Tar creation function. Written by dmitrig01. danielebarchiesi@4: * danielebarchiesi@4: * @param $name danielebarchiesi@4: * Filename of the file to be tarred. danielebarchiesi@4: * @param $contents danielebarchiesi@4: * String contents of the file. danielebarchiesi@4: * danielebarchiesi@4: * @return danielebarchiesi@4: * A string of the tar file contents. danielebarchiesi@4: */ danielebarchiesi@4: function features_tar_create($name, $contents) { danielebarchiesi@4: /* http://www.mkssoftware.com/docs/man4/tar.4.asp */ danielebarchiesi@4: /* http://www.phpclasses.org/browse/file/21200.html */ danielebarchiesi@4: $tar = ''; danielebarchiesi@4: $bigheader = $header = ''; danielebarchiesi@4: if (strlen($name) > 100) { danielebarchiesi@4: $bigheader = pack("a100a8a8a8a12a12a8a1a100a6a2a32a32a8a8a155a12", danielebarchiesi@4: '././@LongLink', '0000000', '0000000', '0000000', danielebarchiesi@4: sprintf("%011o", strlen($name)), '00000000000', danielebarchiesi@4: ' ', 'L', '', 'ustar ', '0', danielebarchiesi@4: '', '', '', '', '', ''); danielebarchiesi@4: danielebarchiesi@4: $bigheader .= str_pad($name, floor((strlen($name) + 512 - 1) / 512) * 512, "\0"); danielebarchiesi@4: danielebarchiesi@4: $checksum = 0; danielebarchiesi@4: for ($i = 0; $i < 512; $i++) { danielebarchiesi@4: $checksum += ord(substr($bigheader, $i, 1)); danielebarchiesi@4: } danielebarchiesi@4: $bigheader = substr_replace($bigheader, sprintf("%06o", $checksum)."\0 ", 148, 8); danielebarchiesi@4: } danielebarchiesi@4: $header = pack("a100a8a8a8a12a12a8a1a100a6a2a32a32a8a8a155a12", // book the memorie area danielebarchiesi@4: substr($name,0,100), // 0 100 File name danielebarchiesi@4: '100644 ', // File permissions danielebarchiesi@4: ' 765 ', // UID, danielebarchiesi@4: ' 765 ', // GID, danielebarchiesi@4: sprintf("%11s ", decoct(strlen($contents))), // Filesize, danielebarchiesi@4: sprintf("%11s", decoct(REQUEST_TIME)), // Creation time danielebarchiesi@4: ' ', // 148 8 Check sum for header block danielebarchiesi@4: '', // 156 1 Link indicator / ustar Type flag danielebarchiesi@4: '', // 157 100 Name of linked file danielebarchiesi@4: 'ustar ', // 257 6 USTAR indicator "ustar" danielebarchiesi@4: ' ', // 263 2 USTAR version "00" danielebarchiesi@4: '', // 265 32 Owner user name danielebarchiesi@4: '', // 297 32 Owner group name danielebarchiesi@4: '', // 329 8 Device major number danielebarchiesi@4: '', // 337 8 Device minor number danielebarchiesi@4: '', // 345 155 Filename prefix danielebarchiesi@4: ''); // 500 12 ?? danielebarchiesi@4: danielebarchiesi@4: $checksum = 0; danielebarchiesi@4: for ($i = 0; $i < 512; $i++) { danielebarchiesi@4: $checksum += ord(substr($header, $i, 1)); danielebarchiesi@4: } danielebarchiesi@4: $header = substr_replace($header, sprintf("%06o", $checksum)."\0 ", 148, 8); danielebarchiesi@4: $tar = $bigheader.$header; danielebarchiesi@4: danielebarchiesi@4: $buffer = str_split($contents, 512); danielebarchiesi@4: foreach ($buffer as $item) { danielebarchiesi@4: $tar .= pack("a512", $item); danielebarchiesi@4: } danielebarchiesi@4: return $tar; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Export var function -- from Views. danielebarchiesi@4: */ danielebarchiesi@4: function features_var_export($var, $prefix = '', $init = TRUE, $count = 0) { danielebarchiesi@4: if ($count > 50) { danielebarchiesi@4: watchdog('features', 'Recursion depth reached in features_var_export', array()); danielebarchiesi@4: return ''; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: if (is_object($var)) { danielebarchiesi@4: $output = method_exists($var, 'export') ? $var->export() : features_var_export((array) $var, '', FALSE, $count+1); danielebarchiesi@4: } danielebarchiesi@4: else if (is_array($var)) { danielebarchiesi@4: if (empty($var)) { danielebarchiesi@4: $output = 'array()'; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $output = "array(\n"; danielebarchiesi@4: foreach ($var as $key => $value) { danielebarchiesi@4: // Using normal var_export on the key to ensure correct quoting. danielebarchiesi@4: $output .= " " . var_export($key, TRUE) . " => " . features_var_export($value, ' ', FALSE, $count+1) . ",\n"; danielebarchiesi@4: } danielebarchiesi@4: $output .= ')'; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: else if (is_bool($var)) { danielebarchiesi@4: $output = $var ? 'TRUE' : 'FALSE'; danielebarchiesi@4: } danielebarchiesi@4: else if (is_int($var)) { danielebarchiesi@4: $output = intval($var); danielebarchiesi@4: } danielebarchiesi@4: else if (is_numeric($var)) { danielebarchiesi@4: $output = floatval($var); danielebarchiesi@4: } danielebarchiesi@4: else if (is_string($var) && strpos($var, "\n") !== FALSE) { danielebarchiesi@4: // Replace line breaks in strings with a token for replacement danielebarchiesi@4: // at the very end. This protects whitespace in strings from danielebarchiesi@4: // unintentional indentation. danielebarchiesi@4: $var = str_replace("\n", "***BREAK***", $var); danielebarchiesi@4: $output = var_export($var, TRUE); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $output = var_export($var, TRUE); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: if ($prefix) { danielebarchiesi@4: $output = str_replace("\n", "\n$prefix", $output); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: if ($init) { danielebarchiesi@4: $output = str_replace("***BREAK***", "\n", $output); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: return $output; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Helper function to return an array of t()'d translatables strings. danielebarchiesi@4: * Useful for providing a separate array of translatables with your danielebarchiesi@4: * export so that string extractors like potx can detect them. danielebarchiesi@4: */ danielebarchiesi@4: function features_translatables_export($translatables, $indent = '') { danielebarchiesi@4: $output = ''; danielebarchiesi@4: $translatables = array_filter(array_unique($translatables)); danielebarchiesi@4: if (!empty($translatables)) { danielebarchiesi@4: $output .= "{$indent}// Translatables\n"; danielebarchiesi@4: $output .= "{$indent}// Included for use with string extractors like potx.\n"; danielebarchiesi@4: sort($translatables); danielebarchiesi@4: foreach ($translatables as $string) { danielebarchiesi@4: $output .= "{$indent}t(" . features_var_export($string) . ");\n"; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return $output; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Get a summary storage state for a feature. danielebarchiesi@4: */ danielebarchiesi@4: function features_get_storage($module_name) { danielebarchiesi@4: // Get component states, and array_diff against array(FEATURES_DEFAULT). danielebarchiesi@4: // If the returned array has any states that don't match FEATURES_DEFAULT, danielebarchiesi@4: // return the highest state. danielebarchiesi@4: $states = features_get_component_states(array($module_name), FALSE); danielebarchiesi@4: $states = array_diff($states[$module_name], array(FEATURES_DEFAULT)); danielebarchiesi@4: $storage = !empty($states) ? max($states) : FEATURES_DEFAULT; danielebarchiesi@4: return $storage; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Wrapper around features_get_[storage] to return an md5hash of a normalized danielebarchiesi@4: * defaults/normal object array. Can be used to compare normal/default states danielebarchiesi@4: * of a module's component. danielebarchiesi@4: */ danielebarchiesi@4: function features_get_signature($state = 'default', $module_name, $component, $reset = FALSE) { danielebarchiesi@4: switch ($state) { danielebarchiesi@4: case 'cache': danielebarchiesi@4: $codecache = variable_get('features_codecache', array()); danielebarchiesi@4: return isset($codecache[$module_name][$component]) ? $codecache[$module_name][$component] : FALSE; danielebarchiesi@4: case 'default': danielebarchiesi@4: $objects = features_get_default($component, $module_name, TRUE, $reset); danielebarchiesi@4: break; danielebarchiesi@4: case 'normal': danielebarchiesi@4: $objects = features_get_normal($component, $module_name, $reset); danielebarchiesi@4: break; danielebarchiesi@4: } danielebarchiesi@4: if (!empty($objects)) { danielebarchiesi@4: $objects = (array) $objects; danielebarchiesi@4: _features_sanitize($objects); danielebarchiesi@4: return md5(_features_linetrim(features_var_export($objects))); danielebarchiesi@4: } danielebarchiesi@4: return FALSE; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Set the signature of a module/component pair in the codecache. danielebarchiesi@4: */ danielebarchiesi@4: function features_set_signature($module, $component, $signature = NULL) { danielebarchiesi@4: $var_codecache = variable_get('features_codecache', array()); danielebarchiesi@4: $signature = isset($signature) ? $signature : features_get_signature('default', $module, $component, TRUE); danielebarchiesi@4: $var_codecache[$module][$component] = $signature; danielebarchiesi@4: variable_set('features_codecache', $var_codecache); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Processing semaphore operations. danielebarchiesi@4: */ danielebarchiesi@4: function features_semaphore($op, $component) { danielebarchiesi@4: // Note: we don't use variable_get() here as the inited variable danielebarchiesi@4: // static cache may be stale. Retrieving directly from the DB narrows danielebarchiesi@4: // the possibility of collision. danielebarchiesi@4: $semaphore = db_query("SELECT value FROM {variable} WHERE name = :name", array(':name' => 'features_semaphore'))->fetchField(); danielebarchiesi@4: $semaphore = !empty($semaphore) ? unserialize($semaphore) : array(); danielebarchiesi@4: danielebarchiesi@4: switch ($op) { danielebarchiesi@4: case 'get': danielebarchiesi@4: return isset($semaphore[$component]) ? $semaphore[$component] : FALSE; danielebarchiesi@4: case 'set': danielebarchiesi@4: $semaphore[$component] = REQUEST_TIME; danielebarchiesi@4: variable_set('features_semaphore', $semaphore); danielebarchiesi@4: break; danielebarchiesi@4: case 'del': danielebarchiesi@4: if (isset($semaphore[$component])) { danielebarchiesi@4: unset($semaphore[$component]); danielebarchiesi@4: variable_set('features_semaphore', $semaphore); danielebarchiesi@4: } danielebarchiesi@4: break; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Get normal objects for a given module/component pair. danielebarchiesi@4: */ danielebarchiesi@4: function features_get_normal($component, $module_name, $reset = FALSE) { danielebarchiesi@4: static $cache; danielebarchiesi@4: if (!isset($cache) || $reset) { danielebarchiesi@4: $cache = array(); danielebarchiesi@4: } danielebarchiesi@4: if (!isset($cache[$module_name][$component])) { danielebarchiesi@4: features_include(); danielebarchiesi@4: $code = NULL; danielebarchiesi@4: $module = features_get_features($module_name); danielebarchiesi@4: danielebarchiesi@4: // Special handling for dependencies component. danielebarchiesi@4: if ($component === 'dependencies') { danielebarchiesi@4: $cache[$module_name][$component] = isset($module->info['dependencies']) ? array_filter($module->info['dependencies'], 'module_exists') : array(); danielebarchiesi@4: } danielebarchiesi@4: // All other components. danielebarchiesi@4: else { danielebarchiesi@4: $default_hook = features_get_default_hooks($component); danielebarchiesi@4: if ($module && $default_hook && isset($module->info['features'][$component]) && features_hook($component, 'features_export_render')) { danielebarchiesi@4: $code = features_invoke($component, 'features_export_render', $module_name, $module->info['features'][$component], NULL); danielebarchiesi@4: $cache[$module_name][$component] = isset($code[$default_hook]) ? eval($code[$default_hook]) : FALSE; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // Clear out vars for memory's sake. danielebarchiesi@4: unset($code); danielebarchiesi@4: unset($module); danielebarchiesi@4: } danielebarchiesi@4: return isset($cache[$module_name][$component]) ? $cache[$module_name][$component] : FALSE; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Get defaults for a given module/component pair. danielebarchiesi@4: */ danielebarchiesi@4: function features_get_default($component, $module_name = NULL, $alter = TRUE, $reset = FALSE) { danielebarchiesi@4: static $cache = array(); danielebarchiesi@4: $alter = !empty($alter); // ensure $alter is a true/false boolean danielebarchiesi@4: features_include(); danielebarchiesi@4: features_include_defaults($component); danielebarchiesi@4: $default_hook = features_get_default_hooks($component); danielebarchiesi@4: $components = features_get_components(); danielebarchiesi@4: danielebarchiesi@4: // Collect defaults for all modules if no module name was specified. danielebarchiesi@4: if (isset($module_name)) { danielebarchiesi@4: $modules = array($module_name); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: if ($component === 'dependencies') { danielebarchiesi@4: $modules = array_keys(features_get_features()); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $modules = array(); danielebarchiesi@4: foreach (features_get_component_map($component) as $component_modules) { danielebarchiesi@4: $modules = array_merge($modules, $component_modules); danielebarchiesi@4: } danielebarchiesi@4: $modules = array_unique($modules); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // Collect and cache information for each specified module. danielebarchiesi@4: foreach ($modules as $m) { danielebarchiesi@4: if (!isset($cache[$component][$alter][$m]) || $reset) { danielebarchiesi@4: // Special handling for dependencies component. danielebarchiesi@4: if ($component === 'dependencies') { danielebarchiesi@4: $module = features_get_features($m); danielebarchiesi@4: $cache[$component][$alter][$m] = isset($module->info['dependencies']) ? $module->info['dependencies'] : array(); danielebarchiesi@4: unset($module); danielebarchiesi@4: } danielebarchiesi@4: // All other components danielebarchiesi@4: else { danielebarchiesi@4: if ($default_hook && module_hook($m, $default_hook)) { danielebarchiesi@4: $cache[$component][$alter][$m] = call_user_func("{$m}_{$default_hook}"); danielebarchiesi@4: if (is_array($cache[$component][$alter][$m])) { danielebarchiesi@4: $alter_type = features_get_components('alter_type', $component); danielebarchiesi@4: if ($alter && (!isset($alter_type) || $alter_type == FEATURES_ALTER_TYPE_NORMAL)) { danielebarchiesi@4: if ($alter_hook = features_get_default_alter_hook($component)) { danielebarchiesi@4: drupal_alter($alter_hook, $cache[$component][$alter][$m]); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $cache[$component][$alter][$m] = FALSE; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $cache[$component][$alter][$m] = FALSE; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // A specific module was specified. Retrieve only its components. danielebarchiesi@4: if (isset($module_name)) { danielebarchiesi@4: return isset($cache[$component][$alter][$module_name]) ? $cache[$component][$alter][$module_name] : FALSE; danielebarchiesi@4: } danielebarchiesi@4: // No module was specified. Retrieve all components. danielebarchiesi@4: $all_defaults = array(); danielebarchiesi@4: if (isset($cache[$component][$alter])) { danielebarchiesi@4: foreach (array_filter($cache[$component][$alter]) as $module_components) { danielebarchiesi@4: $all_defaults = array_merge($all_defaults, $module_components); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return $all_defaults; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Get a map of components to their providing modules. danielebarchiesi@4: */ danielebarchiesi@4: function features_get_default_map($component, $attribute = NULL, $callback = NULL, $reset = FALSE) { danielebarchiesi@4: static $map = array(); danielebarchiesi@4: danielebarchiesi@4: global $features_ignore_conflicts; danielebarchiesi@4: if ($features_ignore_conflicts) { danielebarchiesi@4: return FALSE; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: features_include(); danielebarchiesi@4: features_include_defaults($component); danielebarchiesi@4: if ((!isset($map[$component]) || $reset) && $default_hook = features_get_default_hooks($component)) { danielebarchiesi@4: $map[$component] = array(); danielebarchiesi@4: foreach (module_implements($default_hook) as $module) { danielebarchiesi@4: if ($defaults = features_get_default($component, $module)) { danielebarchiesi@4: foreach ($defaults as $key => $object) { danielebarchiesi@4: if (isset($callback)) { danielebarchiesi@4: if ($object_key = $callback($object)) { danielebarchiesi@4: $map[$component][$object_key] = $module; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: elseif (isset($attribute)) { danielebarchiesi@4: if (is_object($object) && isset($object->{$attribute})) { danielebarchiesi@4: $map[$component][$object->{$attribute}] = $module; danielebarchiesi@4: } danielebarchiesi@4: elseif (is_array($object) && isset($object[$attribute])) { danielebarchiesi@4: $map[$component][$object[$attribute]] = $module; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: elseif (!isset($attribute) && !isset($callback)) { danielebarchiesi@4: if (!is_numeric($key)) { danielebarchiesi@4: $map[$component][$key] = $module; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: return FALSE; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return isset($map[$component]) ? $map[$component] : FALSE; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Retrieve an array of features/components and their current states. danielebarchiesi@4: */ danielebarchiesi@4: function features_get_component_states($features = array(), $rebuild_only = TRUE, $reset = FALSE) { danielebarchiesi@4: static $cache; danielebarchiesi@4: if (!isset($cache) || $reset) { danielebarchiesi@4: $cache = array(); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: $features = !empty($features) ? $features : array_keys(features_get_features()); danielebarchiesi@4: danielebarchiesi@4: // Retrieve only rebuildable components if requested. danielebarchiesi@4: features_include(); danielebarchiesi@4: $components = array_keys(features_get_components()); danielebarchiesi@4: if ($rebuild_only) { danielebarchiesi@4: foreach ($components as $k => $component) { danielebarchiesi@4: if (!features_hook($component, 'features_rebuild')) { danielebarchiesi@4: unset($components[$k]); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: foreach ($features as $feature) { danielebarchiesi@4: $cache[$feature] = isset($cache[$feature]) ? $cache[$feature] : array(); danielebarchiesi@4: if (module_exists($feature)) { danielebarchiesi@4: foreach ($components as $component) { danielebarchiesi@4: if (!isset($cache[$feature][$component])) { danielebarchiesi@4: $normal = features_get_signature('normal', $feature, $component, $reset); danielebarchiesi@4: $default = features_get_signature('default', $feature, $component, $reset); danielebarchiesi@4: $codecache = features_get_signature('cache', $feature, $component, $reset); danielebarchiesi@4: $semaphore = features_semaphore('get', $component); danielebarchiesi@4: danielebarchiesi@4: // DB and code states match, there is nothing more to check. danielebarchiesi@4: if ($normal == $default) { danielebarchiesi@4: $cache[$feature][$component] = FEATURES_DEFAULT; danielebarchiesi@4: danielebarchiesi@4: // Stale semaphores can be deleted. danielebarchiesi@4: features_semaphore('del', $component); danielebarchiesi@4: danielebarchiesi@4: // Update code cache if it is stale, clear out semaphore if it stale. danielebarchiesi@4: if ($default != $codecache) { danielebarchiesi@4: features_set_signature($feature, $component, $default); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: // Component properly implements exportables. danielebarchiesi@4: else if (!features_hook($component, 'features_rebuild')) { danielebarchiesi@4: $cache[$feature][$component] = FEATURES_OVERRIDDEN; danielebarchiesi@4: } danielebarchiesi@4: // Component does not implement exportables. danielebarchiesi@4: else { danielebarchiesi@4: if (empty($semaphore)) { danielebarchiesi@4: // Exception for dependencies. Dependencies are always rebuildable. danielebarchiesi@4: if ($component === 'dependencies') { danielebarchiesi@4: $cache[$feature][$component] = FEATURES_REBUILDABLE; danielebarchiesi@4: } danielebarchiesi@4: // All other rebuildable components require comparison. danielebarchiesi@4: else { danielebarchiesi@4: // Code has not changed, but DB does not match. User has DB overrides. danielebarchiesi@4: if ($codecache == $default) { danielebarchiesi@4: $cache[$feature][$component] = FEATURES_OVERRIDDEN; danielebarchiesi@4: } danielebarchiesi@4: // DB has no modifications to prior code state (or this is initial install). danielebarchiesi@4: else if (empty($codecache) || $codecache == $normal) { danielebarchiesi@4: $cache[$feature][$component] = FEATURES_REBUILDABLE; danielebarchiesi@4: } danielebarchiesi@4: // None of the states match. Requires user intervention. danielebarchiesi@4: else if ($codecache != $default) { danielebarchiesi@4: $cache[$feature][$component] = FEATURES_NEEDS_REVIEW; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: // Semaphore is still within processing horizon. Do nothing. danielebarchiesi@4: if ((REQUEST_TIME - $semaphore) < FEATURES_SEMAPHORE_TIMEOUT) { danielebarchiesi@4: $cache[$feature][$component] = FEATURES_REBUILDING; danielebarchiesi@4: } danielebarchiesi@4: // A stale semaphore means a previous rebuild attempt did not complete. danielebarchiesi@4: // Attempt to complete the rebuild. danielebarchiesi@4: else { danielebarchiesi@4: $cache[$feature][$component] = FEATURES_REBUILDABLE; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // Filter cached components on the way out to ensure that even if we have danielebarchiesi@4: // cached more data than has been requested, the return value only reflects danielebarchiesi@4: // the requested features/components. danielebarchiesi@4: $return = $cache; danielebarchiesi@4: $return = array_intersect_key($return, array_flip($features)); danielebarchiesi@4: foreach ($return as $k => $v) { danielebarchiesi@4: $return[$k] = array_intersect_key($return[$k], array_flip($components)); danielebarchiesi@4: } danielebarchiesi@4: return $return; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Helper function to eliminate whitespace differences in code. danielebarchiesi@4: */ danielebarchiesi@4: function _features_linetrim($code) { danielebarchiesi@4: $code = explode("\n", $code); danielebarchiesi@4: foreach ($code as $k => $line) { danielebarchiesi@4: $code[$k] = trim($line); danielebarchiesi@4: } danielebarchiesi@4: return implode("\n", $code); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * "Sanitizes" an array recursively, performing two key operations: danielebarchiesi@4: * - Sort an array by its keys (assoc) or values (non-assoc) danielebarchiesi@4: * - Remove any null or empty values for associative arrays (array_filter()). danielebarchiesi@4: */ danielebarchiesi@4: function _features_sanitize(&$array) { danielebarchiesi@4: if (is_array($array)) { danielebarchiesi@4: $is_assoc = _features_is_assoc($array); danielebarchiesi@4: if ($is_assoc) { danielebarchiesi@4: ksort($array, SORT_STRING); danielebarchiesi@4: $array = array_filter($array); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: sort($array); danielebarchiesi@4: } danielebarchiesi@4: foreach ($array as $k => $v) { danielebarchiesi@4: if (is_array($v)) { danielebarchiesi@4: _features_sanitize($array[$k]); danielebarchiesi@4: if ($is_assoc && empty($array[$k])) { danielebarchiesi@4: unset($array[$k]); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Is the given array an associative array. This basically extracts the keys twice to get the danielebarchiesi@4: * numerically ordered keys. It then does a diff with the original array and if there is no danielebarchiesi@4: * key diff then the original array is not associative. danielebarchiesi@4: * danielebarchiesi@4: * NOTE: If you have non-sequential numerical keys, this will identify the array as assoc. danielebarchiesi@4: * danielebarchiesi@4: * Borrowed from: http://www.php.net/manual/en/function.is-array.php#96724 danielebarchiesi@4: * danielebarchiesi@4: * @return True is the array is an associative array, false otherwise danielebarchiesi@4: */ danielebarchiesi@4: function _features_is_assoc($array) { danielebarchiesi@4: return (is_array($array) && (0 !== count(array_diff_key($array, array_keys(array_keys($array)))) || count($array)==0)); danielebarchiesi@4: }