'; var_dump($sql['new friend']); echo "
the age of {$sql['new friend']['name']} is $age
"; } See examples how to configure and use APIQL */ class apiql{ public static $syntax = array(); public static $callback = ''; public static $isValid = false; public static $data = null; public static $error = ''; public static $events = array( 'afterQuery' =>array(), 'beforeQuery' =>array(), 'error' =>array(), ); private static $config=array( 'check_func_exists'=>false, 'display_errors'=>false, 'error_level'=>E_USER_WARNING ); /* * access: public * description: sets your class configuration to prevent * or not function exixtence,display errors * vars: what [string] the configuration param name * val [bool/int] the value of the param * *returns: null */ public static function set($what='',$val=false){ $what=(string)$what; if(isset(self::$config[$what])){ self::$config[$what] = $val; } } /* * access: public * * description: get your class configuration param value * * vars: what [string] the configuration param name * *returns: the value on success, null on faailure */ public static function get($what=''){ $what=(string)$what; if(isset(self::$config[$what])){ return self::$config[$what]; } return null; } /* * access: public * vars: model [string] the query model to parse * fn [string] the function name to bind the query * * return: true on success or false on failure */ public static function register($model='',$fn=''){ $elements=explode('/',$model); foreach($elements as $el){ $tmp=array( 'model' =>trim($el), 'required' =>false, 'data_type' =>array(), 'callback' =>'' ); preg_match('/^ (?P!|\?) (?P[a-z0-9_-\s]+) (?P(\[([a-z\s]+,?)+\])|$) /xi',$el,$blocks); if(empty($blocks)){ self::trigger_error("apiql query => invalid block `{$el}`"); return self::reset(); } $tmp['required']=($blocks['required'] == '!')?true:false; if(trim($blocks['data_type']) != ''){ $data=explode(',',substr($blocks['data_type'],1,-1)); foreach($data as $type){ $type=trim($type); if(preg_match('/^(string|int|float|null|boolean|json|array|php)$/i',$type)){ $tmp['data_type'][]=strtolower($type); } else{ self::trigger_error("apiql query => invalid data type `{$type}`"); return self::reset(true); } } } self::$syntax[$blocks['name']]=$tmp; } if(!function_exists($fn) && self::$config['check_func_exists'] == true){ self::trigger_error("apiql query => undefined function `{$fn}` as callback"); return self::reset(); } else{ self::$callback = $fn; } self::compile(); } /* * access: private * description: try to compile the query ad register it * vars: none * *returns: true on success or false on failure */ private static function compile(){ $const=array(); foreach(self::$syntax as $el){ $const[]=$el['model']; } $const =md5(implode('/',$const)); $const ='apiql_'.$const; $value =array('query'=>self::$syntax,'callback'=>self::$callback); $value =serialize($value); if(!defined($const)){ self::reset(); return define($const,$value,true); } else{ self::trigger_error("unable to register query: query is already defined"); return self::reset(true); } } /* * access: public * * description: execute your query and bind its results on your binded function * * vars: req [string] the query to be executed * [,opt1,op2,opt3...] all extra arguments will be * passed to the binded function 'as is' * * return: your query results on success or false on failure */ public static function query($req=''){ $extra_data=func_get_args(); $not_extra=array_shift($extra_data); $requested_reg='(?)'; $constants=get_defined_constants(true); $constants=(isset($constants['user']))?$constants['user']:array(); foreach($constants as $ckey=>$const){ if(preg_match('/^apiql_[a-f0-9]{32}$/i',$ckey)){ //extrasc infos on constant name $qry_data=unserialize($const); //var_dump($qry_data); $reg=array(); $cnt=0; foreach($qry_data['query'] as $name=>$el){ $data_type=''; if(!empty($el['data_type'])){ $data_type=array(); foreach($el['data_type'] as $data_value){ if($data_value=='string') $data_type[] ='\'[^\']*\''; elseif($data_value=='int') $data_type[] ='((\'\-?\d+\')|\-?(\d+))'; elseif($data_value=='float') $data_type[] ='((\'\-?(\d+)?\.\d+\')|\-?(\d+)?\.\d+)'; elseif($data_value=='null') $data_type[] ='(null)'; elseif($data_value=='boolean') $data_type[] ='(true|false)'; elseif($data_value=='json') $data_type[] ='(\{[^\}]+\})'; elseif($data_value=='array') $data_type[] ='(\[[^\]]+\])'; elseif($data_value=='php') $data_type[] ='(\(.+\))'; } $data_type[]='apiql::query\([[:space:]]*("[^"]*"|\'[^\']*\')[[:space:]]*\)'; $data_type='[[:space:]]+(?P'.implode($data_type,'|').')'; } $name=preg_quote($name,'/'); $name=preg_replace('/[[:space:]]+/','[[:space:]]+',$name); if($cnt > 0) $name='[[:space:]]+'.$name; $reg_el="(?P{$name}{$data_type})"; if($el['required'] == false) $reg_el.='?'; $reg[]=$reg_el; $cnt++; } $reg ="/^[[:space:]]*\n".implode("\n",$reg)."\n[[:space:]]*$/smix"; preg_match($reg,$req,$check); if(!empty($check)){ //check if the query contains subqueries. //in this case we execute subqueries and //each query is replaced with its result preg_match_all('/ (?P apiql::query\( [[:space:]]* (?P ("[^"]*"|\'[^\']*\') ) [[:space:]]* \) )/Usmix',$req,$sub_queries); if(isset($sub_queries['query_block']) && !empty($sub_queries['query_block'])){ foreach($sub_queries['query_block'] as $sq_i=>$sq_str){ $sq_query =substr($sub_queries['query'][$sq_i],1,-1); $sq_res =apiql::query($sq_query); $sq_res =(is_string($sq_res)) ?"'".str_replace("'","\\'",$sq_res)."'" :apiql::string($sq_res); $req=str_replace($sq_str,$sq_res,$req); } } } preg_match($reg,$req,$check); if(!empty($check)){ preg_match($reg,$req,$check); $fn_data=array(); foreach($check as $ck_key=>$ck_val){ if(strpos($ck_key,'name_') === 0){ $fn_data[$ck_key] =$ck_val; $data_index =substr($ck_key,5); if(isset($check['data_'.$data_index])){ $fn_data[$ck_key] =str_replace($check['data_'.$data_index],'',$fn_data[$ck_key]); $fn_data[$ck_key] =trim($fn_data[$ck_key]); $fn_data['data_'.$data_index] =$check['data_'.$data_index]; } } } $fn_args=array('names'=>array(),'data'=>array()); foreach($fn_data as $f_key=>$f_value){ if(strpos($f_key,'name_') === 0){ $f_cnt=str_replace('name_','',$f_key); $fn_args['names'][$f_cnt]=trim($f_value); if(isset($fn_data['data_'.$f_cnt])){ $test_data=trim($fn_data['data_'.$f_cnt]); if(preg_match('#^({.*}|\[.*\])$#',$test_data)){ $test_data=self::JSONdecode($test_data,true); } else{ $test_data=self::checkStr($test_data); } $fn_args['data'][$f_cnt]=$test_data; } } } //var_dump($fn_args); self::$data=array(); foreach($fn_args['names'] as $fk=>$name){ //array_key_exists is better then isset, //it is a good solution for not make confusion with null values $to_name=(array_key_exists($fk, $fn_args['data']) ) ?$fn_args['data'][$fk] :false; self::$data[$name]=$to_name; } //beforeQuery events self::fireEvents('beforeQuery'); $send_to_fn=$extra_data; array_unshift($send_to_fn,self::$data); $out=call_user_func_array($qry_data['callback'],$send_to_fn); //afterQuery events self::fireEvents('afterQuery'); return $out; } } } self::trigger_error("apiql query => undefined query"); return self::reset(true); } /* * access: private * * description: convert a string rappresentation of a boolean,number or null * value in its effective value * * vars: str [string] the string to be checked * * return: its real value and type */ private static function checkStr($str){ if(preg_match('#^\'.*\'$#',$str)) $str=substr($str,1,-1); if(is_numeric($str)){ eval('$check = '.$str.';'); return $check; } if(strtolower($str) == 'true') return true; if(strtolower($str) == 'false') return false; if(strtolower($str) == 'null') return null; return $str; } /* * access: public * * description: bind an event function on before or after a query * * vars: ev [string] the event to register * can be 'beforeQuery','afterQuery' or 'error' * * returns: null */ public static function addEvent($ev,$fn_name){ $ev =strtolower((string)$ev); $ev =str_replace('query','Query',trim($ev)); $fn_name=strtolower(trim((string)$fn_name)); if(isset(self::$events[$ev])){ self::$events[$ev][]=$fn_name; } } /* * access: public * * description: remove a specific function from the event specified * * vars: ev [string] the event from where the function will be deleterd * fn_name [string] the function to be removed from. * * returns: null */ public static function removeEvent($ev,$fn_name){ $ev =strtolower((string)$ev); $ev =str_replace('query','Query',trim($ev)); $fn_name=strtolower(trim((string)$ev)); $key =array_search($fn_name,self::$events[$ev]); if($key !== false){ unset(self::$events[$ev][$key]); } } /* * access: public * * description: remove all function from the events specified * * vars: [ev1,ev2,ev3] [string] the events from where the functions will be removed * * returns: null */ public static function removeEvents(){ $evs=func_get_args(); if(!empty($evs)) $evs=array_keys(self::$events); foreach($evs as $ev){ $ev=strtolower((string)$ev); $ev=str_replace('query','Query',trim($ev)); if(isset(self::$events[$ev]) && !empty(self::$events[$ev])){ self::$events[$ev]=array(); } } return true; } /* * access: public * * description: fires all functions from the events specified * * vars: [ev1,ev2,ev3] [string] the events from where the functions will be fired * * returns: null */ public static function fireEvents(){ $evs=func_get_args(); if(empty($evs)) $evs=array_keys(self::$events); foreach($evs as $ev){ $ev=strtolower((string)$ev); $ev=str_replace('query','Query',trim($ev)); if(isset(self::$events[$ev]) && !empty(self::$events[$ev])){ foreach(self::$events[$ev] as $fn){ if(function_exists($fn)) $ev_data=call_user_func($fn,self::$data); } } } } /* * access: private * * description: reset the internal data except the configuration * * vars: asError [bool] if true, fires the 'error' event * * returns: false if is called as actiot to execute after an error, or true */ private static function reset($asError=false){ self::$syntax = array(); self::$callback = ''; self::$isValid = false; self::$data = null; self::$error = ''; if($asError) { self::fireEvents('error'); return false; } return true; } /* * access: private * * description: internal error trigger * * vars: msg [string] the string to be triggered * * returns: null */ private static function trigger_error($msg){ self::$error=$msg; if(self::$config['display_errors'] == true){ trigger_error($msg,self::$config['error_level']); } } /* * access: public * * description: convert a not well formed json string in valid php json object * * vars: json [string] the json string to be analyzed * assoc [bool] if true, force as object * returns: a well formed json string, valid for PHP */ public static function JSONdecode($json, $assoc = false){ $json = str_replace(array("\n","\r"),"",$json); //remove trailing commas and encode in utf8 $json=preg_replace('/,\s*([\]}])/m', '$1', $json); $json = preg_replace('/([{,])(\s*)([^"]+?)\s*:/','$1"$3":',$json); $json = preg_replace('/([{,])"\'([^"]+?)\'":/','$1"$2":',$json); $json = preg_replace('/:(\s*)([^"]+?)\s*([,}])/',':"$2"$3',$json); $json = preg_replace('/:"\'([^"]+?)\'"([,}])/',':"$1"$2',$json); $json = preg_replace('/:"(null|true|false|\d+\.\d+|\d+|\.\d+)"([,}])/',':$1$2',$json); return json_decode($json,$assoc); } /* * access: public * * description: convert the passed argument as json string * * vars: obj the value to be converted * * returns: converted value */ public static function string($obj=null){ if(is_object($obj)){ $out=get_object_vars($obj); $out=json_encode($out); } elseif(is_array($obj)) {$out=json_encode($obj);} elseif(is_bool($obj)) {$out=($obj == true)?'true':'false';} elseif(is_null($obj)) {$out= 'null';} else {$out=(string)$obj;} return $out; } } ?>