Skip to content

Commit

Permalink
admin_config_set: Revised process_complex_value() function
Browse files Browse the repository at this point in the history
The new code features an improved regex, which deals more efficiently
with the parsing of multi-dimensional and associative arrays.

Regex are defined as static variables for better performance with
repeated and recursive calls

Known issue: an invalid definition like 'array(array(1,2)=>array(3,4))'
is not properly parsed.

Fixes #13298
  • Loading branch information
dregad committed Mar 8, 2013
1 parent 8b98640 commit 0719f32
Showing 1 changed file with 54 additions and 20 deletions.
74 changes: 54 additions & 20 deletions adm_config_set.php
Expand Up @@ -134,35 +134,69 @@
* @return parsed variable
*/
function process_complex_value( $p_value, $p_trimquotes = false ) {
static $s_regex_array = null;
static $s_regex_string = null;
static $s_regex_element = null;

$t_value = trim( $p_value );

if( preg_match('/^array[\s]*\((.*)\)$/s', $t_value, $t_match ) === 1 ) {
# Parsing regex initialization
if( is_null( $s_regex_array ) ) {
$s_regex_array = '^array[\s]*\((.*)\)$';
$s_regex_string =
# unquoted string (word)
'[\w]+' . '|' .
# single-quoted string
"'(?:[^'\\\\]|\\\\.)*'" . '|' .
# double-quoted string
'"(?:[^"\\\\]|\\\\.)*"';
# The following complex regex will parse individual array elements,
# taking into consideration sub-arrays, associative arrays and single,
# double and un-quoted strings
# @TODO dregad reverse pattern logic for sub-array to avoid match on array(xxx)=>array(xxx)
$s_regex_element = '('
# Main sub-pattern - match one of
. '(' .
# sub-array: ungreedy, no-case match ignoring nested parenthesis
'(?:(?iU:array\s*(?:\\((?:(?>[^()]+)|(?1))*\\))))' . '|' .
$s_regex_string
. ')'
# Optional pattern for associative array, back-referencing the
# above main pattern
. '(?:\s*=>\s*(?2))?' .
')';
}

if( preg_match( "/$s_regex_array/s", $t_value, $t_match ) === 1 ) {
# It's an array - process each element
$t_elements = special_split( $t_match[1] );
$t_processed = array();
foreach( $t_elements as $key => $element ) {
if ( !trim( $element ) ) {
continue;
}
# Check if element is associative array
$t_split = preg_split("/\(.*\)|'.*'|=>/", $element, 2, PREG_SPLIT_NO_EMPTY);
if ( count( $t_split ) == 2 ) {
# associative array
$t_new_key = constant_replace( trim( $t_split[0], " \t\n\r\0\x0B\"'" ) );
$t_new_value = process_complex_value( $t_split[1], true );
$t_processed[$t_new_key] = $t_new_value;
} else {
# regular array
$t_new_value = process_complex_value( $element );
$t_processed[$key] = $t_new_value;

if( preg_match_all( "/$s_regex_element/", $t_match[1], $t_elements ) ) {
foreach( $t_elements[0] as $key => $element ) {
if ( !trim( $element ) ) {
# Empty element - skip it
continue;
}
# Check if element is associative array
preg_match_all( "/($s_regex_string)\s*=>\s*(.*)/", $element, $t_split );
if( !empty( $t_split[0] ) ) {
# associative array
$t_new_key = constant_replace( trim( $t_split[1][0], " \t\n\r\0\x0B\"'" ) );
$t_new_value = process_complex_value( $t_split[2][0], true );
$t_processed[$t_new_key] = $t_new_value;
} else {
# regular array
$t_new_value = process_complex_value( $element );
$t_processed[$key] = $t_new_value;
}
}
}
return $t_processed;
} else {
# Scalar value
if( $p_trimquotes ) {
$t_value = trim( $t_value, " \t\n\r\0\x0B\"'" );
}
}
return constant_replace( $t_value );
}
}
Expand Down Expand Up @@ -195,9 +229,9 @@ function special_split ( $p_string ) {
} else if( $character == "'" ) {
$t_inside_quote = !$t_inside_quote;
} else if( $character == "\\" ) {
// escape character
# escape character
$t_escape_next = true;
// keep the escape if the string will be going through another recursion
# keep the escape if the string will be going through another recursion
if( $t_paren_level > 0 ) {
$t_array_element .= $character;
}
Expand Down

0 comments on commit 0719f32

Please sign in to comment.