'AGAINST', 'and' => 'AND', 'as' => 'AS', 'charset' => 'CHARACTER SET', 'collate' => 'COLLATE', 'create' => 'CREATE', 'database' => 'DATABASE', 'delete' => 'DELETE FROM', 'distinct' => 'DISTINCT', 'drop' => 'DROP', 'engine' => 'ENGINE', '!exist' => 'IF NOT EXISTS', 'exist' => 'IF EXISTS', 'explain' => 'EXPLAIN', 'from' => 'FROM', 'grant' => 'GRANT', 'grant_option' => 'GRANT OPTION', 'group_by' => 'GROUP BY', 'having' => 'HAVING', 'in' => 'IN', 'insert' => 'INSERT INTO', 'join' => 'JOIN', 'join-left' => 'LEFT', 'join-right' => 'RIGHT', 'join-inner' => 'INNER', 'join-full' => 'FULL', 'join-self' => 'SELF', #'join-outer' => 'OUTER', 'join-cross' => 'CROSS', 'like' => 'LIKE', 'limit' => 'LIMIT', 'match' => 'MATCH', 'not_in' => 'NOT IN', 'on' => 'ON', 'on_table' => 'ON TABLE', 'or' => 'OR', 'order_by' => 'ORDER BY', 'offset' => 'OFFSET', 'revoke' => 'REVOKE', 'select' => 'SELECT', 'set' => 'SET', 'table' => 'TABLE', 'table_charset' => 'DEFAULT CHARSET', 'to' => 'TO', 'update' => 'UPDATE', 'values' => 'VALUES', 'where' => 'WHERE' ]; static $escape_char = '`'; protected $compiled = []; public static function select($param) { $param = Arrayobj::make($param); return static::prepare_array([ $param->if_has('explain' , static::$syntax['explain']), static::$syntax['select'], $param->if_has('distinct' , static::$syntax['distinct']), static::group_fields($param['fields'] ?: '*'), static::$syntax['from'], static::full_tablename($param), static::prepare_join($param['join']), static::prepare_where($param['where'], false, $param->ternary('escaped', true)), $param->if_has('group_by' , static::prepare_group($param['group_by'], $param['alias'] ?? null)), $param->if_has('having' , static::$syntax['having']." {$param['having']}"), /* @todo UNION | INTERSECT | EXCEPT GOES HERE !*/ $param->if_has('order_by' , static::prepare_order($param['order_by'], $param['alias'] ?? null)), static::prepare_limit($param) ]); } /** * This function will translate parameters into a "create database" or "create table", depending * on given param. * * @param array $param 'subject': table or database * * @return Type Description */ public static function create($param) { $param = Arrayobj::make($param); return strtolower( $param['subject'] ) === 'table' ? static::create_table($param) : static::create_database($param); } public static function create_table($param) { $param = is_array($param) ? Arrayobj::make($param) : $param; return static::prepare_array([ static::$syntax['create'], static::$syntax['table'], $param->if_has('!exist', static::$syntax['!exist']), static::full_tablename($param), static::group_create_fields($param->mandatory('fields'), true), $param->if_has('collation' , static::$syntax['collate']." {$param['collation']}" ) ]); } public static function create_database($param) { $param = is_array($param) ? Arrayobj::make($param) : $param; return static::prepare_array([ static::$syntax['create'], static::$syntax['database'], $param->if_has('!exist', static::$syntax['!exist']), static::escape( $param->mandatory('database') ) ]); } public static function insert($param) { $param = Arrayobj::make($param); $field_label = static::group_fields( $param->mandatory('fields'), true, true ); $field_values = static::group_values( $param->mandatory('values'), $param['escaped'] ?: false ); return static::prepare_array([ static::$syntax['insert'], static::full_tablename($param), $field_label, static::$syntax['values'], $field_values ]); } public static function grant($param) { $param = Arrayobj::make($param); $field_label = static::group_fields( $param->mandatory('privileges') ); $users = static::group_fields( $param->mandatory('users') ); return static::prepare_array([ static::$syntax['grant'], $field_label, static::$syntax['on_table'], static::full_tablename($param), static::$syntax['to'], $users, $param->if_has('grant_option', static::$syntax['grant_option']) ]); } public static function delete($param) { $param = Arrayobj::make($param); return static::prepare_array([ static::$syntax['delete'], static::full_tablename($param), static::prepare_where($param['where'], false, $param->ternary('escaped', true)), static::prepare_order($param), static::prepare_limit($param) ]); } public static function update($param) { $param = Arrayobj::make($param); $fields = static::group_values_and_fields($param->mandatory('fields'), $param->mandatory('values')); return static::prepare_array([ static::$syntax['update'], static::full_tablename($param), static::$syntax['set'], $fields, static::prepare_where($param['where']) ]); } public static function drop($param) { $param = Arrayobj::make($param); return static::prepare_array([ static::$syntax['drop'], $param->exist('table_name') ? static::$syntax['table']." ".static::full_tablename($param) : static::$syntax['database']." ".static::escape($param->mandatory('database')) ]); } public static function full_tablename($param) { is_array($param) && ($param = Arrayobj::make($param)); return $param->if_has('database', static::escape($param['database']).".") . static::escape($param->mandatory('table_name')) . $param->if_has('alias', " ".static::$syntax['as']." " . $param['alias']); } public static function group_fields($fields, $enclose = false, $escape = false) { if (is_array($fields)) { return ($enclose ? "(" : "") .implode(', ', $escape ? array_map(function($item){ return static::escape($item); }, $fields) : $fields).($enclose ? ")" : ""); } else { return $escape ? static::escape($fields) : $fields; } } public static function group_create_fields($fields, $enclose = false) { if (is_array($fields)) { $retval = []; foreach($fields as $key => $value) { $retval[] = static::escape($key)." ".$value; } return ($enclose ? "(" : "") .implode(', ', $retval).($enclose ? ")" : ""); } else { return $fields; } } public static function group_values($values, $escaped = false) { $tmp = array_pop($values); array_push($values, $tmp); # Are we dealing with an array of values ? if ( is_array($tmp) ) { $retval = []; foreach($values as $item) { $retval[] = implode(', ', $escaped ? $item : static::escape_values($item) ); } return "(".implode('), (', $retval).")"; } else { return "(".implode(', ', $escaped ? $values : static::escape_values($values)).")"; } } public static function escape_values($values) { $type_function = function(& $item) { switch( $t = gettype($item) ) { case "boolean": $item = $item ? 1 : 0; break; case "double": case "integer": break; case "NULL": $item = "NULL"; break; case "string": $item = "\"$item\""; break; } return $item; }; return is_array($values) ? array_map($type_function, $values) : $type_function($values); } public static function group_values_and_fields($fields, $values) { $retval = []; foreach($fields as $key => $item) { $retval[] = "{$item} = {$values[$key]}"; } return implode(', ', $retval); } public static function prepare_array($sql) { return implode(" ", array_filter($sql)).";"; } public static function prepare_where($where, $recursion = false, $escaped = false) { $retval = []; if (is_array($where)) { $count = count($where); for($i = 0; $i < $count; $i++) { $item = $where[$i]; if ( ! Arrayobj::array_is_associative($item) ) { $retval[] = "(".static::prepare_where($item, true, $escaped).")"; } else { $comparison = (isset($item['comparison']) ? $item['comparison'] : "="); # are we having an IN comparison here ... if ( $is_array = (is_array($item['value']) && count($item['value']) > 1) ) { switch ($item['comparison']) { case '=': $comparison = '='; break; case '!=': $comparison = 'not_in'; break; } } $value = static::group_fields($item['value'], true); switch($comparison) { case 'match': $retval[] = static::$syntax[$comparison].' ('.static::fieldname($item['field'], $item['alias'] ?? null).") ".static::$syntax['against']. " (".(!$escaped || $is_array ? $value : static::escape_values($value))." IN BOOLEAN MODE)". ($i + 1 < $count ? " ".static::$syntax[ isset($item['operator']) ? $item['operator'] : "and" ] : ""); break; default: $retval[] = static::fieldname($item['field'], $item['alias'] ?? null)." " . ( isset(static::$syntax[$comparison]) ? static::$syntax[$comparison] : $comparison) . " ".(!$escaped || $is_array ? $value : static::escape_values($value)). ($i + 1 < $count ? " ".static::$syntax[ isset($item['operator']) ? $item['operator'] : "and" ] : ""); break; } } } } return $retval ? ($recursion ? "" : static::$syntax['where'] . " ") . implode(" ", $retval ) : ""; } public static function prepare_join($joins) { $retval = []; if ( is_array($joins) ) { $count = count($joins); for($i = 0; $i < $count; $i++) { $join = []; $table = Arrayobj::make([ 'table_name' => $joins[$i]['table'], 'alias' => $joins[$i]['alias_right'] ]); $join[] = static::$syntax[ "join-".$joins[$i]['type'] ] ?? $joins[$i]['type']; $join[] = static::$syntax[ 'join' ]; $join[] = static::full_tablename($table); $join[] = static::$syntax[ 'on' ]; foreach($joins[$i]['fields'] as $left_field => $right_field) { #$join[] = $joins[$i]['alias_left'].".".static::escape($left_field); $join[] = static::fieldname($left_field, $joins[$i]['alias_left']); $join[] = $joins[$i]['comparison']; $join[] = static::fieldname($right_field, $joins[$i]['alias_right']); } $retval[] = implode(' ', $join); } } return implode(' ', $retval); } public static function prepare_order($order, $alias = null) { $retval = []; if (is_array($order)) { foreach($order as $item) { $retval[] = static::fieldname($item['field'], $alias).( !empty($item['order']) ? " ".$item['order'] : "" ); } } return $retval ? static::$syntax['order_by']." ".implode(', ', $retval) : ""; } public static function prepare_group($group) { return $group ? static::$syntax['group_by']." ".( is_array($group) ? implode(', ', $group) : $group ) : ""; } public static function prepare_limit($param) { return implode(' ', array_filter([ $param->if_has('limit' , static::$syntax['limit'] ." {$param['limit']}"), $param->if_has('offset', static::$syntax['offset']." {$param['offset']}") ])); } public static function fieldname($field, $alias = null) { return strpos($field, '.') ? $field : (!empty($alias) ? $alias."." : "").static::escape($field); } public static function escape($field) { return static::$escape_char . str_replace(static::$escape_char, '', $field) . static::$escape_char; } }