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
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];
}