You are here

function configuration_context_process_property in Configuration Management 6

Process a single mapping config property. Various properties will mark static data that will be processed later on in the configuration phases.

Parameters

$property: The property needing processed

$value: The value of the property

$context: The context area of the data to process with this property

$config: The config this property=>value pair is a part of

3 calls to configuration_context_process_property()
configuration_apply_map in ./configuration.module
Apply an individual configuration map to the data set object
configuration_find_attributes in ./configuration.module
Helper function to convert tags to attributes
_configuration_replace_tokens in ./configuration.module
Helper function to replace key/values using tokens

File

./configuration.module, line 649
Provide a unified method for defining site configurations abstracted from their data format. Various data formats should be supported via a plugin architecture such as XML, YAML, JSON, PHP

Code

function configuration_context_process_property($property, $value, &$context, &$config = array()) {
  $empty = $context->empty;

  // This is a list of properties that can take an empty match
  $empty_properties = array(
    '#alias',
    '#required',
    '#create',
    '#default',
    '#default callback',
    '#default php',
    '#default path',
    '#default value',
  );

  // This is a list of properties that do not modify the context object
  // which will save any integrity checks later on
  $no_modify_propertiers = array(
    '#include',
    '#action',
    '#action callback',
    '#required',
    '#options',
    '#options callback',
  );

  // If this is an empty context and not in the empty property list, return immediately
  if ($empty && !in_array($property, $empty_properties)) {
    return;
  }
  switch ($property) {
    case '#include':
      $file = './' . $value;
      if (!is_file($file)) {
        configuration_set_error('pre-validate: include', array(
          '!file' => $file,
        ));
      }
      else {
        @(require_once $file);
        $context->include = $file;
      }
      break;
    case '#alias':

      // TODO Figure out how to deal with the $matches array syncing with new matches here
      if ($empty) {
        if (is_scalar($value)) {
          $value = array(
            $value,
          );
        }
        foreach ($value as $alias) {

          // Find the alias, if it exists rename it
          if (array_key_exists($alias, $context->parent->item)) {

            // Find the alias context
            for ($i = 0; isset($context->parent->children[$i]); $i++) {
              $change =& $context->parent->children[$i];
              if ($change->key == $alias) {
                configuration_context_process_property('#key', $context->key, $change, $config);
                break 2;
              }
            }
          }
        }
      }

      // Deal with secondary keys here. No need to check if it already exists
      // since they can exist multiple times
      for ($i = 0; isset($context->parent->children[$i]); $i++) {
        $check =& $context->parent->children[$i];
        if (($pos = in_array($check->secondary_key, $value)) !== false) {
          $check->secondary_key = $context->secondary_key;
        }
      }
      break;
    case '#default callback':
      if ($empty) {
        if (is_array($value)) {
          $args = reset($value);
          $value = key($value);
        }
        if (function_exists($value)) {
          if (isset($args)) {
            $context->parent->item[$context->key] = call_user_func_array($value, $args);
          }
          else {
            $context->parent->item[$context->key] = $value($context);
          }
          $context->empty = false;
        }
      }
      break;
    case '#default php':
      if ($empty) {

        // Make sure it is set and then get the default and reset it to that
        $context->parent->item[$context->key] = null;
        $php = $value;
        $value =& $context->parent->item[$context->key];
        $new = eval($php);
        if (isset($new)) {
          $context->parent->item[$context->key] = $new;
        }
        $context->empty = false;
      }
      break;
    case '#default path':
      if ($empty && ($matches = configuration_fetch($value, $context)) && count($matches) == 1) {
        $context->parent->item[$context->key] = $matches[0]->item;
        $context->empty = false;
      }
      break;
    case '#default':
    case '#default value':
    case '#create':
      if ($empty) {

        // TODO Think about converting all other context keys to ->_ like ->_parent ->_item
        if ($context->_attribute) {
          $context->item = $value;
        }
        else {
          $context->parent->item[$context->key] = $value;
          $context->item =& $context->parent->item[$context->key];
        }
        $context->empty = false;
      }
      break;
    case '#key callback':
      if (function_exists($value)) {
        $value = $value($context->item);
      }
      else {
        configuration_set_error(t('An invalid callback was specified: !callback', array(
          '!callback' => $value,
        )));
        break;
      }
    case '#key path':
      if ($property == '#key path') {
        if (is_array($value)) {
          $path = key($value);
          $default = current($value);
        }
        else {
          $path = $value;
        }
        $match = configuration_fetch($path, $context);
        if (count($match) == 1 && is_scalar($match[0]->item)) {
          $value = $match[0]->item;
        }
        else {
          if ($config['#key default']) {
            $value = $config['#key default'];
          }
          else {
            break;
          }
        }
      }
    case '#key php':
      if ($property == '#key php') {
        $value = eval($value);
      }
      break;
    case '#key':
      if (array_key_exists($value, $context->parent->item)) {

        // Key exists already
        // TODO Is it a problem that the key exists already? Quite possibly....
        break;
      }
      $key = $context->key;
      $context->parent->item[$value] =& $context->item;

      // We need to keep our current context object. check_context should fix the trace paths below
      $context->key = $value;

      // A plain unset is sufficient even with indexed arrays because check_context will pick it up and fix
      unset($context->parent->item[$key]);
      break;
    case '#array':

      // TODO Figure out how to deal with associative vs indexed arrays
      if (!is_array($context->item) || !array_key_exists(0, $context->item)) {
        if (is_null($context->item) || is_array($context->item && empty($context->item))) {
          $context->item = array();
        }
        else {
          $context->item = array(
            $context->item,
          );
        }
      }
      break;
    case '#assoc':
      if (!is_array($value) || array_key_exists(0, $context->item)) {
        configuration_set_error('assoc array');
      }
      break;

    // TODO Test both copy and move
    case '#copy':
      $destinations = configuration_fetch($value, $context);
      foreach ($destinations as &$dest) {

        // TODO We are making it impossible to overwrite an existing item. Maybe we should make it possible? This applies to #move too
        if ($dest->empty) {
          $dest->item = $context->item;
        }
      }
      break;
    case '#move':

      // TODO Moving/Copying to the same array level as the current item can cause problems with the current matches set. Maybe make it impossible to do so or think of a solution.
      $dest = configuration_fetch($value, $context);
      if (count($dest) == 1 && ($dest =& $dest[0])) {
        $parent =& $context->parent;
        $key = $context->key;

        // Move the necessary dest info over here
        foreach (array_diff(array_keys((array) $dest), array(
          'children',
          'item',
        )) as $key) {

          // TODO Is there room to do better memory cleanup here or does it not matter?
          $context->{$key} =& $dest->{$key};
        }

        // Make sure the old child pointer points to the new context
        for ($i = 0; isset($context->parent->children[$i]); $i++) {
          $check =& $context->parent->children[$i];
          if ($check === $dest) {
            $context->parent->children[$i] =& $context;
            break;
          }
        }

        // Make sure the moved data exists in the right place of the original array
        $context->parent->item[$context->key] =& $context->item;

        // Remove the item here. check_context will make sure contexts are removed
        // We can't remove if we moved to this items parent
        // TODO Make it work so you can move two parents up
        if ($parent !== $dest) {
          unset($parent->item[$key]);
        }

        // Fix all the children trace paths
        configuration_fix_context_trace($context);
      }
    case '#required':
      if ($context->empty) {
        $path = implode('/', array_merge($context->trace, array(
          $context->key,
        )));
        configuration_set_error('pre-validate: required', array(
          '!tag' => $path,
        ));
      }
      else {
        $context->required = true;
      }
      break;
    case '#context key':
      if (is_scalar($context->item)) {
        $context->parent->secondary_key = $context->item;
      }
      break;
    case '#attribute':
      if (!is_scalar($context->item)) {
        $path = implode('/', $context->trace);
        configuration_set_error('pre-validate: attribute', array(
          '!tag' => $path,
        ));
      }
      else {
        if ($context->key[0] == CONFIGURATION_ATTRIBUTE_KEY) {
          $key = substr($context->key, 1);
        }
        else {
          $key = $context->key;
        }
        if (!configuration_reserved_attribute($key)) {
          $context->parent->{$key} = $context->item;
          unset($context->parent->item[$context->key]);
        }
      }
      break;
    case '#options callback':
      if (function_exists($value)) {
        $value = $value($context);
      }
    case '#options':
      if (is_scalar($value)) {
        $value = array(
          $value,
        );
      }
      if (!in_array($context->item, $value)) {
        if (!($context->item === '' && !$context->required)) {
          $path = implode('/', $context->trace);
          configuration_set_error('pre-validate: options', array(
            '!path' => $path,
            '!options' => implode(', ', $value),
          ));
          break;
        }
      }
    case '#value alias':
      foreach ($value as $real => $aliases) {
        foreach ($aliases as $alias) {
          if ($context->item == $alias) {
            $context->item = $real;
            break 2;
          }
        }
      }
      break;
    case '#value callback':
      if (function_exists($value)) {
        $value($context->item, $context);
      }
      else {
        configuration_set_error('value callback', array(
          '@callback' => $value,
        ));
      }
      break;
    case '#value php':
      $php = $value;
      $value =& $context->item;
      $new = eval($php);
      if (isset($new)) {
        $context->item = $new;
      }
      break;
    case '#value path':
      if (($matches = configuration_fetch($value, $context)) && count($matches) == 1) {
        $context->item = $matches[0]->item;
      }
      break;
    case '#value':
      $context->item = $value;
      break;
    case '#id callback':
      if (function_exists($value)) {
        $identifiers =& configuration_get_data('identifiers');
      }
      break;
    case '#id php':
      break;
    case '#id path':
      break;

    // Not used/supported.
    // TODO Remove?
    case '#build callback':
      if (function_exists($value)) {
        $context->build_data = $value($context);
      }
      break;
    case '#build php':
      $context->build_data = eval($value);
      break;
    case '#action':
      if (!is_array($value)) {
        $value = array(
          $value,
        );
      }
      $actions =& configuration_get_data('actions');
      foreach ($value as $action) {
        $action[] = array(
          $action,
          null,
          &$context,
        );
      }
      break;
    case '#action callback':
      if (function_exists($value)) {
        $actions =& configuration_get_data('actions');
        $context->action_callback = $value;
        $actions[] = array(
          null,
          &$context,
        );
      }
      else {
        configuration_set_error('missing action callback', array(
          '@callback' => $value,
        ));
      }
      break;
    case '#enable module':
      if (is_scalar($context->item)) {
        $enable =& configuration_get_data('enable modules');
        $enable[] = $context->item;
      }
      break;
    case '#disable module':
      if (is_scalar($context->item)) {
        $disable =& configuration_get_data('disable modules');
        $disable[] = $context->item;
      }
      break;
    case '#delete':
      unset($context->parent->item[$context->key]);
      break;
  }

  // Update the context object to reflect any possible new changes
  if (!in_array($property, $no_modify_propertiers)) {
    configuration_check_context($context);
  }
}