/**
  * Parses the innards of the table definition block for info
  * @param   string  $aql
  * @param   array   $parent     (if in a subquery)
  * @return  array   { fields, subqueries, objects, clauses }
  */
 public function inner($aql, $parent = array())
 {
     $tmp = array();
     $subqueries = $this->split_tables($aql, true);
     $subqueries = $subqueries[0];
     if (is_array($subqueries)) {
         $subs = array();
         $sub = '';
         foreach ($subqueries as $k => $v) {
             if (stripos(trim($v), "'") > 2 || stripos(trim($v), "'") === false) {
                 // FOR MULTIPLE SUBQUERIES
                 $aql = str_replace($v, '', $aql);
                 if (!preg_match('/\\},$/', trim($v))) {
                     $sub .= $v;
                 } else {
                     $sub .= $v;
                     $sub = substr($sub, 0, -1);
                     $subs[] = $sub;
                     $sub = '';
                 }
             }
         }
         $subs[] = $sub;
         foreach ($subs as $s) {
             $sub = $this->init($s, $parent);
             if (!empty($sub)) {
                 $keys = array_keys($sub);
                 $tmp['subqueries'][$keys[0]] = $sub;
             }
         }
     }
     // remove opening brace and before and last brace and whitespace
     $aql = trim(substr(substr($aql, 0, -1), strpos($aql, '{') + 1));
     // get clauses
     foreach (array_reverse(self::$clauses) as $cl) {
         $pattern = sprintf('/(?:\\b%s\\b)%s/i', $cl, "(?=(?:(?:[^']*+'){2})*+[^']*+\\z)");
         $split = preg_split($pattern, $aql, 2);
         $aql = $split[0];
         if ($split[1]) {
             $tmp[$cl] = $split[1];
         }
     }
     preg_match_all(self::$object_pattern, $aql, $matches);
     $aql = str_replace($matches[0], '', $aql);
     foreach ($matches['model'] as $k => $v) {
         $primary_table = aql::get_primary_table($v);
         $constructor_arg = $matches['param'][$k] ?: $primary_table . '_id';
         $object_tmp = array('model' => $v, 'primary_table' => $primary_table, 'constructor argument' => $constructor_arg);
         $tmp_as = $matches['as'][$k] ?: $v;
         if ($matches['sub'][$k]) {
             $object_tmp['plural'] = true;
             $object_tmp['sub_where'] = $this->subquery_where($primary_table, $primary_table, $parent['table'], $parent['as']);
         }
         $tmp['objects'][$tmp_as] = $object_tmp;
     }
     $i = 1;
     $fields = explodeOnComma($aql);
     array_walk($fields, function ($field, $_, $o) use($parent, &$tmp, &$i) {
         $add_field = function ($alias, $value, $type = 'fields') use(&$tmp) {
             $tmp[$type][$alias] = $value;
         };
         $field = trim($field);
         if (empty($field)) {
             return;
         }
         if ($field == '*') {
             $fields = $o->get_table_fields($parent['table']);
             if (is_array($fields)) {
                 foreach ($fields as $f) {
                     $tmp['fields'][$f] = $parent['as'] . '.' . $f;
                 }
             }
             return;
         }
         $as = array_map('trim', preg_split("/\\bas\\b{$o->not_in_quotes}/", $field));
         $alias = $as[1] ?: $as[0];
         if (strpos($alias, "'") !== false) {
             $alias = 'field_' . $i;
         }
         if (!$as[0] || !$alias) {
             return;
         }
         if (preg_match("/(case|when){$o->not_in_quotes}/im", $as[0])) {
             // this is a case when
             $add_field($alias, trim($o->parse_case_when($as[0], $parent['as'])));
         } else {
             if (strpos($as[0], ')') !== false) {
                 // this is a "function" we call it an aggregate for now
                 $a = array_map('trim', explode('(', $as[0]));
                 if (!empty($a[0])) {
                     $alias = $alias == $as[0] ? $a[0] : $alias;
                     if ($tmp['aggregates'][$alias]) {
                         $j = '1';
                         while (true) {
                             if ($tmp['aggregates'][$alias . '_' . $j]) {
                                 $j++;
                                 continue;
                             }
                             $alias = $alias . '_' . $i;
                             break;
                         }
                     }
                     // end if alias is already taken.
                     $add_field($alias, $o->aggregate_add_table_name($parent['as'], $as[0]), 'aggregates');
                 } else {
                     $add_field($alias, $as[0]);
                 }
             } else {
                 // regular field
                 $add_field($alias, trim($o->add_table_name($parent['as'], $as[0])));
             }
         }
         $i++;
     }, $this);
     $tmp['fields'] = $tmp['fields'] ?: array();
     $tmp['aggregates'] = $tmp['aggregates'] ?: array();
     foreach (array('order by', 'group by') as $cl) {
         $tmp[$cl] = $this->check_clause(explodeOnComma($tmp[$cl]), $parent, array_merge($tmp['fields'], $tmp['aggregates']));
     }
     $tmp['where'] = preg_split('/\\band\\b(?=(?:(?:(?:[^()]++|\\.)*+[()]){2})*+(?:[^())]++|\\.)*+$)/i', $tmp['where']);
     return $tmp;
 }
 /**
  * Given the aql array, append table names to strings in the clause aray if they are
  * missing
  * @param   array   $aql_array
  * @param   array   $clause_array
  */
 public static function check_clause_array($aql_array, $clause_array)
 {
     $first = reset($aql_array);
     $clauses = aql2array::$clauses;
     $comparisons = aql2array::$comparisons;
     foreach ($clause_array as $k => $v) {
         if (in_array($k, $clauses)) {
             $clause_array = array($first['as'] => $clause_array);
             break;
         }
     }
     foreach ($clause_array as $table => $v) {
         if (!is_array($v)) {
             continue;
         }
         foreach ($v as $clause => $value) {
             if ($clause == 'where') {
                 $value = is_array($value) ? $value : array($value);
                 $arr = aql2array::prepare_where($value, $aql_array[$table]['table']);
                 $clause_array[$table][$clause] = aql2array::check_where($arr, $aql_array[$table]['as']);
             } else {
                 $value = is_array($value) ? $value : explodeOnComma($value);
                 $clause_array[$table][$clause] = aql2array::check_clause($value, $aql_array[$table], $aql_array[$table]['fields']);
             }
         }
     }
     return $clause_array;
 }