You are here

function configuration_fetch in Configuration Management 6

Find parts of an array based on a semi-compatible xpath syntax.

Returns an array of context items that match and reference the desired parts of an array

Loosely based off of Cake function Set::extract

Supports the following xpath syntax examples

  • /block
  • /menu/id
  • /menu/id=3
  • /modules/*
  • /content/@action
  • /content/@action=update
  • //*
  • //tag
  • //@include
  • /view//display//id
  • /block[module=block]/delta
  • /block[module=block][delta=1]
  • /block[not(module)][not(delta)]

TODO: Test referenced matches and make sure that changes to one match (say a parent of a matched child) are updated in the other matches as well.

Parameters

$path: The configuration path (xpath) that should lead to the matches

$context: The previously built context object that is used to traverse the data

$return_empty: If TRUE this fetch operation will return empty objects for each place where a potential match would have been

Return value

Return an indexed array of matched context items

7 calls to configuration_fetch()
configuration_apply_map in ./configuration.module
Apply an individual configuration map to the data set object
configuration_context_process_property in ./configuration.module
Process a single mapping config property. Various properties will mark static data that will be processed later on in the configuration phases.
configuration_load_includes in ./configuration.module
Load all include files including the module configuration component files supplied by the configuration framework
configuration_process_action in ./configuration.module
Execute a single action
configuration_replace_tokens in ./configuration.module
Find and replace identifier and token keys and values

... See full list

File

./configuration.module, line 1382
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_fetch($path, &$context, $return_empty = false) {

  // TODO Think a bit more about the necessity of $match_record
  static $match_record;
  if ($path === '/') {
    $matches = array(
      &$context,
    );
    $match_record[$path] = $matches;
    return $match_record[$path];
  }
  else {
    if ($path[0] !== '/') {
      $path = '/' . $path;
    }
  }

  // Start the list of contexts
  $list = array(
    &$context,
  );

  // Create a list of tokens based on the supplied path
  $tokens = array_slice(preg_split('/(?<!=)\\/(?![a-z]*\\])/', $path), 1);
  $all = false;
  $all_matches = array();
  $token = null;
  $previous = null;
  $match = null;
  while (!empty($tokens)) {
    $token = array_shift($tokens);

    // Get any look ahead section
    $look_aheads = array();
    if (preg_match_all('/\\[(.*?)\\]/', $token, $m)) {
      foreach ($m[0] as $remove) {
        $token = str_replace($remove, '', $token);
      }
      $look_aheads = $m[1];
    }

    // TODO Implement better conditionals for each token
    // Currently only supports element=value conditions
    $conditions = array();
    if (preg_match('/(=)(.*)/', $token, $m)) {
      $conditions[$m[1]] = $m[2];
      $token = substr($token, 0, strpos($token, $m[1]));
    }
    $matches = array();
    foreach ($list as &$piece) {
      if ($token === '..') {
        $matches[] =& $piece->parent;
        continue;
      }
      else {
        if ($token === '') {
          $matches[] =& $piece;
          continue;
        }
        else {
          if ($previous === '') {
            for ($i = 0; $i < count($piece->children); $i++) {
              $matches[] =& $piece->children[$i];
            }
          }
        }
      }
      if (is_array($piece->item)) {
        $i = 0;
        while (isset($piece->children[$i])) {
          unset($match);

          // Allow matches to match against a context-only secondary_key
          if ($piece->children[$i]->key === $token || $piece->children[$i]->secondary_key === $token) {
            $match =& $piece->children[$i];
          }
          else {
            if ($token === '*') {
              $match =& $piece->children[$i];
            }
          }

          // Select attributes
          if ($token[0] == '@' && isset($piece->children[$i]->{substr($token, 1)})) {

            //  $match = &$piece->children[$i];
          }
          if (isset($match) && $previous === '') {
            $all_matches[] =& $match;
          }
          else {
            if (isset($match)) {
              $matches[] =& $match;
            }
          }
          $i++;
        }
      }
      else {
        if ($token === '.') {
          $matches[] =& $piece;
        }
      }

      // Select current piece if looking for an attribute
      if ($token[0] == '@' && isset($piece->{substr($token, 1)})) {
        if ($previous === '') {
          $all_matches[] =& $piece;
        }
        else {
          $matches[] =& $piece;
        }
      }
    }

    // Filter matches from the matches list based on our conditions
    foreach ($conditions as $operator => $value) {
      _configuration_array_filter($matches, $operator, $value, $token[0] == '@' ? substr($token, 1) : null);
    }

    // Filter matches based on look-ahead checks
    foreach ($look_aheads as $ahead) {
      _configuration_array_look_ahead($matches, $ahead, $token);
    }

    // Update the context area to the next set of matches to dig into
    // First, continue the same token if we are not done selecting all (//)
    if (!empty($matches) && $previous === '') {
      array_unshift($tokens, $token);
      $list = $matches;
      continue;
    }
    else {
      if (empty($matches) && $previous === '') {
        $matches = $all_matches;
        $all_matches = array();
      }
    }

    // If we are at the end and no matches, tag on empty matches
    if ($return_empty && empty($tokens) && empty($matches) && $token[0] != '@') {
      foreach ($list as &$piece) {

        // Do not attach an empty child if the parent is not an array
        if (!is_array($piece->parent->item)) {
          continue;
        }
        $match = new stdClass();
        $match->empty = TRUE;
        $match->item = null;
        $match->key = !in_array($token, array(
          '*',
          '.',
          '..',
        )) ? $token : null;
        $match->trace = configuration_trace_context($piece);
        $match->parent =& $piece;
        $match->children = array();
        $matches[] = $match;
      }
    }
    else {
      if (empty($tokens) && $token[0] == '@') {
        if ($return_empty && empty($matches)) {
          $attribute_list =& $list;
        }
        else {
          $attribute_list =& $matches;
        }
        for ($i = 0; isset($attribute_list[$i]); $i++) {
          $match =& $attribute_list[$i];
          $attribute = new stdClass();
          $attribute->empty = empty($matches) ? true : false;
          $attribute->_attribute = true;
          $attribute->key = substr($token, 1);
          $attribute->item =& $match->{$attribute->key};
          $attribute->trace = $match->trace;
          $attribute->parent =& $match;
          $attribute->children = array();
          unset($matches[$i]);
          $matches[$i] =& $attribute;
        }
      }
      else {
        if (!empty($tokens) && empty($matches)) {
          break;
        }
        else {
          $list = $matches;
        }
      }
    }
    $previous = $token;
  }

  // Make sure the matches stay recorded here so that changes to the
  // context in check_context will update obtained matches objects
  $match_record[$path] = $matches;

  // Return the list of matches
  return $match_record[$path];
}