Code: Select all
<?php
class Ori{
const UNBRACE = 1;
const BRACE = 2;
const NO_NULLS = 4;
const NO_EMPTY = 8;
const NO_WORDS = 16;
const READABLE = 32;
static function fold($var, $flags = 0, $level = 0){
if ($flags & self::BRACE && $flags & self::UNBRACE) {
$brace = FALSE; //unbrace takes precedence
} elseif ($flags & self::BRACE) {
$brace = TRUE;
} elseif ($flags & self::UNBRACE) {
$brace = FALSE;
} else {
$brace = NULL;
}
$skipNull = $flags & self::NO_NULLS;
$skipEmpty = $flags & self::NO_EMPTY;
$useWords = !($flags & self::NO_WORDS);
$readable = $flags & self::READABLE;
$code = '';
if ($skipEmpty && empty($var)) {
return '';
}
switch(gettype($var)) {
case 'array':
if ($level > 0 || $brace === TRUE || ($brace === NULL && sizeof($var) <= 1)) {
$code .= '(';
if ($readable) { $code .= "\n"; }
} else {
$code .= '';
}
$defaultKey = 0;
foreach ($var as $key => $value) {
if ($skipEmpty && empty($value)
or $skipNull && $value === NULL) {
if (is_int($key) && $key >= $defaultKey) {
$defaultKey = $key + 1; //keep track of index even if value is skipped
}
continue;
}
if ($defaultKey === $key) {
if ($readable) { $code .= str_repeat(' ', 4 * $level); }
$code .= self::fold($value, $flags | self::BRACE, $level + 1).',';
if ($readable) { $code .= "\n"; }
++$defaultKey;
} else {
if ($readable) { $code .= str_repeat(' ', 4 * $level); }
$eq = '=';
if ($readable) {
$eq = ' = ';
}
$code .= self::fold($key, $flags | self::BRACE, $level + 1)
.$eq.self::fold($value, $flags | self::BRACE, $level + 1).',';
if ($readable) { $code .= "\n"; }
}
}
if (!$readable || $level == 0) {
$code = chop($code, ",\n"); //remove unnecessary coma
}
if ($level > 0 || $brace === NULL && sizeof($var) <= 1) {
if ($readable) { $code .= str_repeat(' ', 4 * ($level - 1)); }
$code .= ')';
} elseif ($brace === TRUE) {
if ($readable) { $code .= str_repeat(' ', 4 * ($level - 1)); }
$code .= ')';
} else { //$brace === FALSE
//do nothing
}
return $code;
break;
case 'integer':
case 'double':
return $var;
break;
case 'string':
if (!empty($var) && ctype_alnum($var) && $useWords) {
return $var;
} else {
if (strpos($var, "'") !== FALSE) {
$var = str_replace("'", "''", $var);
}
return "'".$var."'";
}
break;
case 'boolean':
return ($var ? '+' : '-');
break;
default:
if ($skipNull) {
return '';
} else {
return '~';
}
//break;
}
}
static function unfold($str, $flags = 0){
$brace = $flags & self::BRACE;
if (empty($str)) { //could save some time
if ($brace) {
return array();
} else {
return NULL;
}
}
$matches = array();
preg_match_all("/'(.*?)'/s", $str, $matches); //find all quoted strings
$sStack = $matches[1];
$str = preg_replace("/'.*?'/s", "&", $str); //cut them out
if (strpos($str, "'")) {
user_error("Unpaired quotes", E_USER_WARNING);
return;
}
$str = preg_replace('/\s/', '', $str); //cut out whitespace
$str = str_replace(',)', ')', $str); //normalize array closures
preg_match_all('/([^\&\=\,\(\)]+)/', $str, $matches); //find everything else
$xStack = $matches[1];
$str = preg_replace('/[^\&\=\,\(\)]+/', '#', $str); //cut it out
if ($brace || ($str{0} != '(' && strpos($str, ","))) {
$str = '('.$str.')';
}
$heap = array();
$ptr = strlen($str) - 1;
$scalar = TRUE;
while ($ptr >= 0) { //tokenize
if ($str{$ptr} == '&') { //is a quoted string
$string = array_pop($sStack);
while ($ptr > 0 && $str{$ptr - 1} == '&') {
$string = array_pop($sStack)."'".$string; // resolve escaped single quote
$str{$ptr} = '_';
--$ptr;
}
$str{$ptr} = '$';
$heap[$ptr] = $string;
} elseif ($str{$ptr} == '#') {
$value = array_pop($xStack);
$str{$ptr} = '$';
if ($value == '+') {
$heap[$ptr] = TRUE;
} elseif ($value == '-') {
$heap[$ptr] = FALSE;
} elseif ($value == '~') {
$heap[$ptr] = NULL;
} elseif (is_numeric($value)) { //it is a number
$heap[$ptr] = $value + 0;
} else { //is an unquoted string (hopefully)
$heap[$ptr] = (string) $value;
}
} else { //is an array markup character
$scalar = FALSE;
}
--$ptr;
}
if ($scalar) {// if it's a single value do not bother with array processing
return $heap[0];
}
while (($aStart = strrpos($str, '(')) !== FALSE) { //reduce to a single value
$aEnd = strpos($str, ')', $aStart);
if ($aEnd === FALSE) {
user_error("Array beginning at [$aStart] is not closed", E_USER_WARNING);
return;
}
$str{$aStart} = '$'; //array will be reduced to a single variable
$str{$aEnd} = ','; //comma serves as a trigger, so array should now end with one
if ($aStart == $aEnd - 1) { //empty array?
$str{$aEnd} = '_';
$heap[$aStart] = array();
continue;
}
$ptr = $aStart + 1;
if ($str{$ptr} == ',' || $str{$ptr} == '=') {
user_error("Invalid array entry [$ptr]", E_USER_WARNING);
return;
}
$aStack = array();
while ($ptr < $aEnd) {
switch ($str{$ptr}) {
case '=':
$str{$ptr} = '_';
$keyPtr = $ptr - 1;
while ($str{$keyPtr} != '$') {
if ($str{$keyPtr} != '_') {
user_error("Invalid character sequence in array [$keyPtr]", E_USER_WARNING);
return;
}
$str{$keyPtr} = '_';
--$keyPtr;
}
$str{$keyPtr} = '_';
$valPtr = $ptr + 1;
if ($str{$valPtr} != '$') {
user_error("Invalid character sequence in array [$keyPtr]", E_USER_WARNING);
return;
}
$str{$valPtr} = '_';
$aStack[$heap[$keyPtr]]= $heap[$valPtr];
unset($heap[$keyPtr], $heap[$valPtr]);
$commaPtr = $valPtr + 1;
while ($str{$commaPtr} != ',') {
if ($str{$commaPtr} != '_') {
user_error("Invalid character sequence in array [$commaPtr]", E_USER_WARNING);
return;
}
++$commaPtr;
}
$str{$commaPtr} = '_';
$ptr = $commaPtr;
break;
case ',':
$str{$ptr} = '_';
$valPtr = $ptr - 1;
while ($str{$valPtr} != '$') {
if ($str{$valPtr} != '_') {
user_error("Invalid character sequence in array [$valPtr]", E_USER_WARNING);
return;
}
$str{$valPtr} = '_';
--$valPtr;
}
$str{$valPtr} = '_';
$aStack[] = $heap[$valPtr];
unset($heap[$valPtr]);
break;
//do nothing on default
}
++$ptr;
}
$heap[$aStart] = $aStack;
}
return $heap[0]; //everything should have been reduced to a single value
}
}
?>If you want a way for your users to pass complex data structures to your script, it might be quite useful. Instead of writing horribly complex GUI with gadzillion of fields and (possibly) tons of javascripts, you can use a single textarea to create an array of any complexity.