How would I use MySQL's NOW() in PDO prepared statements, or how would I workaround using it while keeping in mind that possibly the Apache Server and Database Server might have either a slightly current time mismatch (few seconds), or in rare occasions might be timezones apart?
I have the following function in my code:
try {
$dbh->insert("users", array(
"email" => $email,
"password" => $password,
"salt" => $salt,
"ingame" => $ingame,
"kiosk" => $kiosk
));
} catch (PDOException $ex) {
error($ex);
}
Which calls:
/**
* Inserts data into a table. Data must be given in key-value pairs.
*
* Example: $dbh->insert("table", array(
* "data1" => $data1,
* "data2" => $data2
* );
*
* #param type $table The table to insert to
* #param type $keyvaluepairs The key-value pairs.
* #return type The statement that this query produced.
*/
public function insert($table, $keyvaluepairs) {
$sql = "INSERT INTO `{$table}` (";
$values_sql = ") VALUES(";
$values = array();
foreach ($keyvaluepairs as $key => $value) {
$sql .= "`${key}`, ";
$values_sql .= "?, ";
$values[] = $value;
}
$query = substr($sql, 0, -2).substr($values_sql, 0, -2).")";
return $this->query($query, $values);
}
Which calls:
//TODO update documentation to show it also handles associative arrays with bindvalue
/**
* Can be called to create a query. Use either unnamed or named placeholders for the prepared statements.
*
* Example: $dbh->query("INSERT INTO table (data1, data2) VALUES(?, ?)", array($data1, $data2));
*
* #param type $query The input query, including unnamed or named placeholders
* #param type $values The input values. If it's not an array, then it will be an one-element array
* #return type The statement constructed by this query
*/
public function query($query, $values = array()) {
if (!is_array($values)) {
$values = array($values);
}
$statement = $this->dbh->prepare($query);
$statement->setFetchMode(PDO::FETCH_OBJ);
$i = 1;
if (is_assoc($values)) {
foreach ($values as $key => $value) {
$statement->bindValue($key, $value);
}
}
else {
foreach ($values as $value) {
$statement->bindValue($i++, $value);
}
}
$statement->execute();
return $statement;
}
Where I have the function:
function is_assoc($array) {
return (bool)count(array_filter(array_keys($array), 'is_string'));
}
So the deal here is that I cannot use custom MySQL queries for the inserts as I've encapsulated those for the sake of easyness, but I still want to be able to insert NOW() without making use of TIMESTAMP / CURRENT_TIMESTAMP().
I hope you understand that this question requires an explained answer as I have already read the 'normal' answers and shown that they do not satisfy my needs.
UPDATE: I have added const SQL_NOW = 1; to my DBH class, however now I want to modify the insert to something like this:
public function insert($table, $keyvaluepairs) {
$sql = "INSERT INTO `{$table}` (";
$values_sql = ") VALUES(";
$values = array();
foreach ($keyvaluepairs as $key => $value) {
if ($value == SELF::SQL_NOW) {
$sql .= "NOW(), ";
}
else {
$sql .= "`${key}`, ";
$values_sql .= "?, ";
$values[] = $value;
}
}
$query = substr($sql, 0, -2).substr($values_sql, 0, -2).")";
return $this->query($query, $values);
}
Which may be a suitable solution, however I cannot use 1 as SQL_NOW value, as it would then fail if I'd want to insert an integer 1. If I go with this solution, what value would SQL_NOW then have? Is it even possible to give it no value?
Excellent question!
This is a perfect example that clearly shows why all these numerous insert(), update() and all other stuff, intended to substitute SQL, are wrong by design.
NOW() is not the only issue you will face with. Just because SQL is not that silly a language as it seems at first glance. And it was invented on purpose. And it's reliability was proven for decades. Means it is not that easy to write whole SQL just as an exercise while learning PHP.
So, the best thing you could do is to keep SQL as is.
What you really, really need is a helper function or two. To automate the repetitive tasks. That is ALL. While SQL have to be left as is. Which will let you to use ALL it's power including use of functions, functions with arguments(!), query modifiers, such as 'INSERT IGNORE' or extended syntax like JOINS.
In case you are using PDO, it is not that simple but feasible.
However, the only proper solution is to use a placeholder of the special type for the SET statement.
$data = array(
"email" => $email,
"password" => $password,
"salt" => $salt,
"ingame" => $ingame,
"kiosk" => $kiosk,
);
$dbh->query("INSERT INTO ?n SET reg = NOW(), ?u","users", $data);
Just one single line to solve all that mess and many, many, many other issues.
Just one single query() method to run any query you want, even REPLACE INTO.
Update.
Just look what are you doing!
You were planning your class to simplify things. But at the moment you are making it more and more complex. In the end you will have a hulking giant which inconsistent syntax for the numerous exceptions scarcely understood even by it's creator and noone else. And which still don't let you run some queries.
Please rethink your design before it's too late.
You can write a wrapper that does this if you pass it an additional argument containing all the column=>SQLfunction values you need.
Mine looks like this:
function pInsertFunc($action, $table, $values, $sqlfunctions)
{
global $pdb;
// There's no way to pass an SQL function like "NOW()" as a PDO parameter,
// so this function builds the query string with those functions. $values
// and $sqlfunctions should be key => value arrays, with column names
// as keys. The $values values will be passed in as parameters, and the
// $sqlfunction values will be made part of the query string.
$value_columns = array_keys($values);
$sqlfunc_columns = array_keys($sqlfunctions);
$columns = array_merge($value_columns, $sqlfunc_columns);
// Only $values become ':paramname' PDO parameters.
$value_parameters = array_map(function($col) {return (':' . $col);}, $value_columns);
// SQL functions go straight in as strings.
$sqlfunc_parameters = array_values($sqlfunctions);
$parameters = array_merge($value_parameters, $sqlfunc_parameters);
$column_list = join(', ', $columns);
$parameter_list = join(', ', $parameters);
$query = "$action $table ($column_list) VALUES ($parameter_list)";
$stmt = $pdb->prepare($query);
$stmt->execute($values);
}
Use it like this:
$values = array(
'ID' => NULL,
'name' => $username,
'address' => $address,
);
$sqlfuncs = array(
'date' => 'NOW()',
);
pInsertFunc("INSERT INTO", "addresses", $values, $sqlfuncs);
The query string that results looks like this:
INSERT INTO addresses (ID, name, address, date) VALUES (:ID, :name, :address, NOW())
Related
I'm trying to develop my functions in PHP (not OOP), to create a CRUD. The goal is to use the same function to any table, but I got stuck already in the first one. Can't figure how to do this.
What I have right now:
// function to avoid injections
function validate($link, $field){
$valid = mysqli_real_escape_string($link, $field);
$valid = strip_tags($valid);
return $valid;
}
// validate input of array
function sqlWithArray($link,$array){
$return = array();
foreach($array as $field=>$val){
$return[$field] = "'".validate($link, $val)."'";
}
return $return;
}
// Multi insert to any table
function InsertDB($link, $table, array $args){
$rows = sqlWithArray($link,$args);
$keys = "(".implode(array_keys($args)," ,").")";
$values = " VALUES (".implode(array_values($args),", ").")";
$query = "INSERT INTO $table $keys $values";
return $link->execute();
}
I was try to using it as:
InsertDB($link, "test_table", $args); //$args is an array
But I keep getting the following error:
PHP Fatal error: Uncaught Error: Call to undefined method mysqli::execute() in includes\functions.php:37
My 37 line is empty, but 36 and 38 are the following:
$query = "INSERT INTO $table $keys $values";
return $link->execute();
What I'm doing wrong here?
Having such a function is a good idea per se. It indicates that you are a programmer in your heart, not just a tinkerer that writes PHP from ready made blocks like a Lego figure. Such a function can greatly improve your code.
But with great power comes great responsibility. Such a function is a constant danger of SQL injection, through table and field names. You should take care of that. Not to mention it should be properly implemented using prepared statements for the data.
First of all, you will need a general purpose function to execute an arbitrary MySQL query using a query and an array of parameters. I have a simple mysqli helper function for you. It will be a basic function to execute all prepared queries:
function prepared_query($mysqli, $sql, $params, $types = "")
{
$types = $types ?: str_repeat("s", count($params));
$stmt = $mysqli->prepare($sql);
$stmt->bind_param($types, ...$params);
$stmt->execute();
return $stmt;
}
Now we can start constructing the SQL query dynamically. For this we will need a function that would escape identifiers
function escape_mysql_identifier($field){
return "`".str_replace("`", "``", $field)."`";
}
It will make identifiers safe, at least as long as you are using Unocode.
Now we can proceed to creation of the correct SQL string. We will need to create an SQL with placeholders, like this:
INSERT INTO `staff` (`name`,`occupation`) VALUES (?,?)
So let's write a function that would create a query like this
function create_insert_query($table, $keys)
{
$keys = array_map('escape_mysql_identifier', $keys);
$fields = implode(",", $keys);
$table = escape_mysql_identifier($table);
$placeholders = str_repeat('?,', count($keys) - 1) . '?';
return "INSERT INTO $table ($fields) VALUES ($placeholders)";
}
And finally we can write the long-sought crud function:
function crud_insert($conn, $table, $data) {
$sql = create_insert_query($table, array_keys($data));
prepared_query($conn, $sql, array_values($data));
}
called like this
$args = ['name' => "D'Artagnan", "occupation" => 'musketeer'];
crud_insert($link, "test_table", $args);
I am working for the first time with prepared statements but I got stuck at this error. For some reason I cant pass the bind types as a parameter?
My code:
function insert($table, $columns = array(), $bindTypes, $values = array()) {
// Store connection.
$connection = connection();
$columnValues = null;
$index = 1;
// Prepare unassigned value string.
foreach ($columns as $column) {
$columnValues .= '?';
if ($index < count($columns)) {
$columnValues .= ', ';
}
$index++;
}
// Debugg purpose: echo query example:
echo "INSERT INTO {$table} (" . implode(', ', $columns) . ") VALUES ({$columnValues})";
// Prepare statement.
$statement = $connection->prepare("INSERT INTO {$table} (" . implode(', ', $columns) . ") VALUES ({$columnValues})");
$statement->bind_param($bindTypes, implode(', ', $values));
}
insert('test', array('name'), 'i', array(1));
If I echo the query example I get "INSERT INTO test (name) VALUES (?)".
Please spare me I am still a learning noob.
PHP > 5.6 version:
function insert($connection, $table, $columns, $values) {
$columnStr = "`".implode("`,`",$columns);
$valueStr = str_repeat('?,', count($values) - 1) . '?';
$types = str_repeat('s', count($values));
$statement = $connection->prepare("INSERT INTO `$table` ($columnStr) VALUES ($valueStr)");
$statement->bind_param($types, ...$values);
$statement->execute();
}
insert($conn, 'test', ['name'], [1]);
Note that you should never ever take table or column names out of user input but always have them hard-coded in your script. Which makes a distinct function for the insert quite useless.
Let's look at the docs:
bool mysqli_stmt::bind_param ( string $types , mixed &$var1 [, mixed &$... ] )
In other words, first argument determines data types and you pass the values as individual parameters starting from the second parameter. However you have this:
$statement->bind_param($bindTypes, implode(', ', $values));
You're combining all individual values into a single comma-separated string, which doesn't make the least sense, esp. if you consider that the function expects arguments by reference.
I see you've flagged your post with MySQLi, which I'm pretty sure is the problem here.
Instead I'd go with PDO on this one, as it allows for a variable number of parameters in a much simpler manner. Allowing you do do something quite similar to example 5 from the PHP manual.
I have tried to create a function for an SQL/PDO Insert query:
function InsertQuery ($table,$cols,$values) {
global $pdo_conn;
foreach($values as $values2) {
$values2 = $values2;
}
$stmt='INSERT into $table (';
foreach($cols as $cols2) {
$stmt.=" ".$cols2.", ";
}
$stmt.=" ) VALUES ( ";
foreach($cols as $cols2) {
$stmt.=" :".$cols2." ";
}
$stmt.=" ) ";
$stmt2 = $pdo_conn->prepare($stmt);
foreach($cols as $cols2) {
$stmt2->bindParam(':$cols2', $cols2);
}
}
but i am getting the error:
Catchable fatal error: Object of class PDOStatement could not be converted to string in /home/integra/public_html/admin/includes/functions.php on line 30
please be patient with me as i am new to PDO and just used to using MySQL
have i put the prepared statement wrong or my foreach loops?
I believe the statement should look like:
$stmt2 = $pdo_conn->prepare('INSERT into $table (col1) values (:val1)');
$stmt2->bindParam(':$val1', $val);
here is how i called my function:
$col=array('col1');
$val=array('val1');
InsertQuery ("table1",$col,$val);
UPDATE:
Ok here is my new code:
global $pdo_conn;
foreach($values as $values2) {
$values2 = $values2;
}
$stmt='INSERT into '.$table.' (';
foreach($cols as $cols2) {
$stmt.=" ".implode(",", $cols2)." ";
}
$stmt.=" ) VALUES ( ";
foreach($cols as $cols2) {
$stmt.=" :".implode(",", $cols2)." ";
}
$stmt.=" ) ";
$stmt2 = $pdo_conn->prepare($stmt);
foreach($cols as $cols2) {
$stmt2->bindParam(':$cols2', $cols2);
}
but i now get the error about the implode:
Warning: implode() [function.implode]: Invalid arguments passed in /home/integra/public_html/admin/includes/functions.php on line 18
Warning: implode() [function.implode]: Invalid arguments passed in /home/integra/public_html/admin/includes/functions.php on line 22
which i think is because there is nothing to implode as there is only one column and one value
Use type hints to ensure the function arguments are arrays:
function InsertQuery ($table, array $cols, array $values) {
Make sure your PDO connection is accessible. If it's global, you have to declare it (credit to #u_mulder):
global $pdo_conn;
The following does nothing, get rid of it:
foreach($values as $values2) {
$values2 = $values2;
}
Use builtin array functions instead of foreach'ing everything:
$col_list = implode(",", $cols);
$param_list = implode(",", array_fill(1,count($cols), "?"));
Variables don't expand inside single-quotes. You need to use double-quotes (credit to #MichaelBerkowski).
Also, use $stmt for a PDOStatement object, and not for the SQL string. That's confusing.
$sql="INSERT into $table ($col_list) VALUES ($param_list)";
$stmt = $pdo_conn->prepare($sql);
You don't need to write a foreach loop to bindParam() in PDO. You can just pass an array of values to execute(). And you already have the values in an array, so it's really easy:
$stmt->execute($values);
}
For extra safety, make sure to delimit the columns, in case someone uses special characters or a SQL keyword in a column name:
$col_list = implode(",", array_map(function ($c) { return "`$c`" }, $cols));
And make sure the values is in a simple array, not an associative array:
$stmt->execute(array_values($values));
Re your comment:
would you be able to show me how to do the same with select, I'm not sure how it would work as if i have a where clause what would i do with it in the function?
One could for example design a function with an argument $where that is an associative array, whose keys are column names, and whose values are the values you're searching for.
Assume the resulting WHERE clause includes these column/value pairs as AND terms, and all the comparisons are equality.
function SelectQuery($table, array $where) {
global $pdo_conn;
$sql = "SELECT * FROM `$table` ";
$values = null;
if ($where) {
$sql .= "WHERE " . implode(" AND ",
array_map(function ($c) { return "`$c` = ?"; } array_keys($where)));
$values = array_values($where);
}
$stmt = $pdo_con->prepare($sql);
$stmt->execute($values);
}
Of course this supports only a small subset of the possible expressions you can have in a SELECT, but I'm just demonstrating a technique here.
If you want a more fully-feature query builder for PHP, take a look at Zend_Db_Sql or Doctrine QueryBuilder or Laravel query builder.
if anything changes on my server and the PDO stops working i can revert back to MySQL while i fix it.
PDO has been stable since 2005 and it will not stop working, unless you change your PHP environment and disable the extension or the mysql driver or something.
Whereas the ext/mysql extension will stop working. It is currently deprecated and PHP has announced they will remove it in a future version of PHP.
There are a few things going on:
First, $cols and $vals should be arrays, but they are strings.
Quick fix is to make them arrays:
$col = array('col1');
$val = array('val1');
InsertQuery("table1", $col, $val);
Second, $pdo_conn is unknown in the function scope. Make it a parameter or a global.
you can also use this for create query from array:
$myArray = array(
'col1' => 'val1',
'col2' => 'val2'
);
$query = "INSERT INTO table (" . implode(", ", array_keys($myArray)) . ") VALUES (" . implode(", ", $myArray) . ")";
maybe usefull for you
http://wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers
this is my insertion method in PDO, it is working 100%. this 'insert' method accepts table, column and value but i want to make it versatile. (i want to insert values with or without the column names)
public function insert($table, $pair = array()){
try{
$Sql = "INSERT INTO $table ( ";
$Sql .= implode(", ", array_keys($pair));
$Sql .= " )";
$Sql .= " VALUES (";
$Sql .= implode(", ", array_fill("0", count($pair), " ?"));
$Sql .= " )";
$array = array_combine(array_keys(array_fill("1", count($pair), ":")), $pair);
$ready = $this->conn->prepare($Sql);
foreach($array as $key => $value)
{
$ready->bindValue($key, $value, PDO::PARAM_STR);
}
$ready->execute();
}
catch(Exception $e){
$this->trace .= " • ". $e->getMessage();
}
}
$new = new community();
echo $new->insert("table", array("Col1" => "value1", "col1" => "value1"));
There are two problems with your function.
It is vulnerable to SQL injection.
It is not flexible. Following the pattern, you are going to have a thousand functions of this kind, which will make your code into mess. Yet it would be always limited subset against real SQL.
What you really need is a function that can create a SET statement out of array and a list of allowed fields.
As a further improvement you may devise a custom placeholder for this statement.
Having these two things you can work out a single general purpose function to run all the DML queries like this:
$db->query("INSERT INTO t SET %u", array("Col1" => "value1", "col1" => "value1"));
It will cost you 3 additional words (insert, into and set), but it will be
readable. Everyone can understand SQL. While to read your function one need a documentation
flexible. It can support any queries and modifiers, not only one single-formed insert.
Every query you wish you can run with this single function:
$data = array("Col1" => "value1", "col1" => "value1");
$db->query("INSERT IGNORE INTO t SET %u", $data);
$db->query("REPLACE INTO t SET %u", $data);
$db->query("DELETE FROM t WHERE id = ?", $id);
// and so on
No dedicated functions actually needed.
Also, you have to always verify a set of fields against a hardcoded white list, to let a user insert only fields they are allowed to. Do not let a user to alter privileges, messages count and so on.
But even without custom placeholder it would require no set of SQL-mapped functions but just a function to create a SET and a general purpose query execution function:
$allowed = array("name","surname","email"); // allowed fields
$sql = "INSERT INTO users SET ".pdoSet($fields,$values);
$stm = $dbh->query($sql ,$values);
I have an array like this
$a = array( 'phone' => 111111111, 'image' => "sadasdasd43eadasdad" );
When I do a var-dump I get this ->
{ ["phone"]=> int(111111111) ["image"]=> string(19) "sadasdasd43eadasdad" }
Now I am trying to add this to the DB using the IN statement -
$q = $DBH->prepare("INSERT INTO user :column_string VALUES :value_string");
$q->bindParam(':column_string',implode(',',array_keys($a)));
$q->bindParam(':value_string',implode(',',array_values($a)));
$q->execute();
The problem I am having is that implode return a string. But the 'phone' column is an integer in the database and also the array is storing it as an integer. Hence I am getting the SQL error as my final query look like this --
INSERT INTO user 'phone,image' values '111111111,sadasdasd43eadasdad';
Which is a wrong query. Is there any way around it.
My column names are dynamic based what the user wants to insert. So I cannot use the placeholders like :phone and :image as I may not always get a values for those two columns. Please let me know if there is a way around this. otherwise I will have to define multiple functions each type of update.
Thanks.
Last time I checked, it was not possible to prepare a statement where the affected columns were unknown at preparation time - but that thing seems to work - maybe your database system is more forgiving than those I am using (mainly postgres)
What is clearly wrong is the implode() statement, as each variable should be handled by it self, you also need parenthesis around the field list in the insert statement.
To insert user defined fields, I think you have to do something like this (at least that how I do it);
$fields=array_keys($a); // here you have to trust your field names!
$values=array_values($a);
$fieldlist=implode(',',$fields);
$qs=str_repeat("?,",count($fields)-1);
$sql="insert into user($fieldlist) values(${qs}?)";
$q=$DBH->prepare($sql);
$q->execute($values);
If you cannot trust the field names in $a, you have to do something like
foreach($a as $f=>$v){
if(validfield($f)){
$fields[]=$f;
$values[]=$v;
}
}
Where validfields is a function that you write that tests each fieldname and checks if it is valid (quick and dirty by making an associative array $valfields=array('name'=>1,'email'=>1, 'phone'=>1 ... and then checking for the value of $valfields[$f], or (as I would prefer) by fetching the field names from the server)
SQL query parameters can be used only where you would otherwise put a literal value.
So if you could see yourself putting a quoted string literal, date literal, or numeric literal in that position in the query, you can use a parameter.
You can't use a parameter for a column name, a table name, a lists of values, an SQL keyword, or any other expressions or syntax.
For those cases, you still have to interpolate content into the SQL string, so you have some risk of SQL injection. The way to protect against that is with whitelisting the column names, and rejecting any input that doesn't match the whitelist.
Because all other answers allow SQL injection. For user input you need to filter for allowed field names:
// change this
$fields = array('email', 'name', 'whatever');
$fieldlist = implode(',', $fields);
$values = array_values(array_intersect_key($_POST, array_flip($fields)));
$qs = str_repeat("?,",count($fields)-1) . '?';
$q = $db->prepare("INSERT INTO events ($fieldlist) values($qs)");
$q->execute($values);
I appreciated MortenSickel's answer, but I wanted to use named parameters to be on the safe side:
$keys = array_keys($a);
$sql = "INSERT INTO user (".implode(", ",$keys).") \n";
$sql .= "VALUES ( :".implode(", :",$keys).")";
$q = $this->dbConnection->prepare($sql);
return $q->execute($a);
You actually can have the :phone and :image fields bound with null values in advance. The structure of the table is fixed anyway and you probably should got that way.
But the answer to your question might look like this:
$keys = ':' . implode(', :', array_keys($array));
$values = str_repeat('?, ', count($array)-1) . '?';
$i = 1;
$q = $DBH->prepare("INSERT INTO user ($keys) VALUES ($values)");
foreach($array as $value)
$q->bindParam($i++, $value, PDO::PARAM_STR, mb_strlen($value));
I know this question has be answered a long time ago, but I found it today and have a little contribution in addition to the answer of #MortenSickel.
The class below will allow you to insert or update an associative array to your database table. For more information about MySQL PDO please visit: http://php.net/manual/en/book.pdo.php
<?php
class dbConnection
{
protected $dbConnection;
function __construct($dbSettings) {
$this->openDatabase($dbSettings);
}
function openDatabase($dbSettings) {
$dsn = 'mysql:host='.$dbSettings['host'].';dbname='.$dbSettings['name'];
$this->dbConnection = new PDO($dsn, $dbSettings['username'], $dbSettings['password']);
$this->dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
function insertArray($table, $array) {
$fields=array_keys($array);
$values=array_values($array);
$fieldlist=implode(',', $fields);
$qs=str_repeat("?,",count($fields)-1);
$sql="INSERT INTO `".$table."` (".$fieldlist.") VALUES (${qs}?)";
$q = $this->dbConnection->prepare($sql);
return $q->execute($values);
}
function updateArray($table, $id, $array) {
$fields=array_keys($array);
$values=array_values($array);
$fieldlist=implode(',', $fields);
$qs=str_repeat("?,",count($fields)-1);
$firstfield = true;
$sql = "UPDATE `".$table."` SET";
for ($i = 0; $i < count($fields); $i++) {
if(!$firstfield) {
$sql .= ", ";
}
$sql .= " ".$fields[$i]."=?";
$firstfield = false;
}
$sql .= " WHERE `id` =?";
$sth = $this->dbConnection->prepare($sql);
$values[] = $id;
return $sth->execute($values);
}
}
?>
dbConnection class usage:
<?php
$dbSettings['host'] = 'localhost';
$dbSettings['name'] = 'databasename';
$dbSettings['username'] = 'username';
$dbSettings['password'] = 'password';
$dbh = new dbConnection( $dbSettings );
$a = array( 'phone' => 111111111, 'image' => "sadasdasd43eadasdad" );
$dbh->insertArray('user', $a);
// This will asume your table has a 'id' column, id: 1 will be updated in the example below:
$dbh->updateArray('user', 1, $a);
?>
public function insert($data = [] , $table = ''){
$keys = array_keys($data);
$fields = implode(',',$keys);
$pre_fields = ':'.implode(', :',$keys);
$query = parent::prepare("INSERT INTO $table($fields) VALUES($pre_fields) ");
return $query->execute($data);
}