I have an issue with my INSERT query, $pdo->execute return false, with error code 00000
Query
string 'INSERT INTO module_test (img_name, description, priority) VALUES(:img_name, :description, :priority)' (length=100)
errorInfo() return:
array (size=3)
0 => string '00000' (length=5)
1 => null
2 => null
Code:
private function Init($query, $parameters = "")
{
# Connect to database
if (!$this->bConnected) {
$this->Connect();
}
try {
# Prepare query
$this->sQuery = $this->pdo->prepare($query);
# Add parameters to the parameter array
$this->bindMore($parameters);
# Bind parameters
if (!empty($this->parameters)) {
foreach ($this->parameters as $param => $value) {
$type = PDO::PARAM_STR;
switch ($value[1]) {
case is_int($value[1]):
$type = PDO::PARAM_INT;
break;
case is_bool($value[1]):
$type = PDO::PARAM_BOOL;
break;
case is_null($value[1]):
$type = PDO::PARAM_NULL;
break;
}
// Add type when binding the values to the column
$this->sQuery->bindValue($value[0], $value[1], $type);
}
}
# Execute SQL
var_dump($query);
var_dump($this->sQuery->execute());
var_dump($this->sQuery->errorInfo());
}
catch (PDOException $e) {
# Write into log and display Exception
echo $this->ExceptionLog($e->getMessage(), $query);
die();
}
# Reset the parameters
$this->parameters = array();
}
public function query($query, $params = null, $fetchmode = PDO::FETCH_ASSOC)
{
$query = trim(str_replace("\r", " ", $query));
$this->Init($query, $params);
$rawStatement = explode(" ", preg_replace("/\s+|\t+|\n+/", " ", $query));
# Which SQL statement is used
$statement = strtolower($rawStatement[0]);
if ($statement === 'select' || $statement === 'show') {
return $this->sQuery->fetchAll($fetchmode);
} elseif ($statement === 'insert' || $statement === 'update' || $statement === 'delete') {
return $this->sQuery->rowCount();
} else {
return NULL;
}
}
public function insert($table, $keyValue)
{
$fieldString = '';
$valueString = '';
$i = 1;
foreach ($keyValue as $key => $currKeyValue)
{
$fieldString .= $key;
$valueString .= ':'.$key;
if($i != count($keyValue))
{
$fieldString .= ', ';
$valueString .= ', ';
}
$i++;
}
$query = 'INSERT INTO '.$table.' ('.$fieldString.') VALUES('.$valueString.')';
$this->query($query, $keyValue);
}
Parameters array
F:\Dev\wamp\wamp64\www\include\class\Database.class.php:216:
array (size=3)
'img_name' => string 'ttt1' (length=4)
'description' => string 'ttt1' (length=4)
'priority' => int 0
I already try this query in phpmyadmin and everything worked well.
If someone know how to solve this?
thanks
PS: sorry for my bad english
PDO is reported not to fill the errorInfo property in certain circumstances.
Instead, you have to make it throw an exception, which is the most reliable way to get the error message. To do so, in your constructor, add this line
$this->pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
Also note that your class is a genuine example of all the mistakes one could make writing a PDO wrapper. I compiled the most popular mistakes in an article, Your first database wrapper's childhood diseases and your class contains every single one of them.
Related
I have a sql UPDATE query in PDO that should update a name and a permissions string.
But instead it just places the id of that row inside all columns.
Here is my code:
public function saveRole($roleID, $name, $permissions)
{
$sql = "UPDATE roles SET name = :name, permissions = :permissions WHERE id = :id";
//this one sets a string variable in the PDO wrapper class
PDO::setSQL($sql);
//this one sets an array inside the PDO wrapper class
PDO::setData([
'name' => $name,
'permissions' => $permissions,
'id' => $roleID,
]);
PDO::execute();
return PDO::getResponse(true);
}
As you see, I've written a wrapper for PDO, which looks like this:
static function execute($sql = null, $data = null, $fetchmode = \PDO::FETCH_OBJ)
{
//check if data and SQL are set in function call, if so, use function call params, if not use class params ($this->SQL & $this->data)
self::connect();
try
{
$stmnt = self::$con->prepare(self::$sql);
$stmnt->setFetchMode($fetchmode);
if (sizeof(self::$data) > 0)
{
foreach (self::$data as $key => $value)
{
$stmnt->bindParam(':' . $key, $value);
}
}
$stmnt->execute();
self::$stmnt = $stmnt;
self::$data = [];
self::$sql = '';
self::$lastResponse = new pdoReturn(true, $stmnt);
return;
} catch (\PDOException $exception)
{
self::$data = [];
self::$sql = '';
self::$lastResponse = new pdoReturn(false, $exception);
return;
}
}
function setSQL($sql) {
if (!is_string($sql))
return false;
if (strlen($sql) == 0)
return false;
$this->sql = $sql;
return true;
}
function setData($data) {
if (!is_array($data))
return false;
$this->data = $data;
return true;
}
As you see, I've written a wrapper for PDO
For the immediate fix, change
$stmnt = self::$con->prepare(self::$sql);
$stmnt->setFetchMode($fetchmode);
if (sizeof(self::$data) > 0)
{
foreach (self::$data as $key => $value)
{
$stmnt->bindParam(':' . $key, $value);
}
}
$stmnt->execute();
to
$stmnt = self::$con->prepare(self::$sql);
$stmnt->setFetchMode($fetchmode);
$stmnt->execute(self::$data);
Then read about your first database wrapper's childhood diseases and fix other issues such as statefulness and error reporting.
i am working on my own restfull api. I have read many article about, but i come out more confused that before.
fist question:
in PUT request is correct to use question mark (?) ex:
http://myapi.me/testApi/v5/create/places?address=Five&building=old
or i must to use
http://myapi.me/testApi/v5/create/places/address=Five&building=old
if the second is the correct way, how i can explode the query string for create my insert statement? my code work well with (?). But curl command does not want to enter the record. So i need to modify the script to reflect the correct way with(/) instead of (?).
Please help...
my code:
$body = file_get_contents("php://input");
$content_type = false;
if(isset($_SERVER['CONTENT_TYPE'])) {
$content_type = $_SERVER['CONTENT_TYPE'];
// echo $content_type;
}
switch($content_type) {
//other case...
case "application/x-www-form-urlencoded":
parse_str($body, $postvars);
foreach($postvars as $field => $value) {
$parameters[$field] = $value;
}
$this->format = "html";
break;
default:
// we could parse other supported formats here
break;
}
$this->parameters = $parameters;
private function createRecord(){
if($this->get_request_method() != "PUT"){
$this->response('',406);
}
$table = strtolower(trim(str_replace("/", "", $_REQUEST['rquest'])));
/*
$Q = explode("/", $_SERVER["rquest"]);
print_r(array_values($Q));
echo "\n";
*/
$uri = $this->parameters;
if($uri){
array_shift($uri);
foreach( $uri as $k => $v )
{
$aKeyPholder[]= $k;
$aKey[]= ':'.$k;
}
$aKeyPholder= implode(',', $aKeyPholder);
$aKey= implode(',', $aKey);
$insert = "INSERT INTO '$table' ($aKeyPholder) VALUES ($aKey)";
// echo "insert=$insert\n";
$stmt = $this->db->prepare($insert);
foreach($uri as $k => &$v){
$stmt->bindParam(':'.$k, $v, PDO::PARAM_STR);
}
//$stmt->execute();
if ($stmt->execute()) { echo "Record succesfully created!";}
$this->response($this->json($success),200);
}
else{
$this->response('',204); // If no records "No Content" status
}
}
I wrote an global function that getting array with keys and values, and inserting it to mysql db. something like this:
function insert_to_db($table, $data, $is_using_id) {
// I'm connecting to db before this code.
global $mysqli;
// .. Checking for errors ..
// .. if using id, remove the id from the values like this:
$columns = array_keys($data);
$values = array_values($data);
if ($is_using_id == true) {
unset($values[0]);
// Reorder the array after unset()
$values = array_merge($values);
}
// ..
// Generating text for use at the mysqli::prepare
$columns_text = "";
$i = 0;
while ($i < count($columns)) {
$column = $columns[$i];
if ($i == 0) {
$columns_text = $column;
} else {
$columns_text = $columns_text.", ".$column;
}
$i++;
}
unset($i);
unset($column);
$values_text = "";
// b_p_f is the $types string for mysqli-stmt::bind_param
$b_p_f = "";
// Generating text for use at the mysqli::prepare
$i = -1;
while ($i < count($values)) {
echo "\$i equals to {$i}<br>";
if ($is_using_id == true && $i == -1) {
// Null because id is calculated automatically by mysql
$values_text = "NULL";
} else if ($is_using_id == false && $i == 0) {
$value = $values[$i];
$values_text = "?";
if (is_numeric($value))
{
$b_p_f = 'i';
} else {
$b_p_f = 's';
}
} else {
$value = $values[$i];
$values_text = $values_text.", ?";
if (is_numeric($value))
{
echo "Value: {$value} Found as numberic<br>";
$b_p_f = $b_p_f.'i';
} else {
echo "Value: {$value} Found as non-numberic<br>";
$b_p_f = $b_p_f.'s';
}
}
$i++;
}
unset($i);
unset($value);
echo "b_p_f:";
var_dump($b_p_f);
echo " values:";
var_dump($values);
$stmt = $mysqli->prepare("INSERT INTO ".$table." (".$columns_text.") VALUES (".$values_text.")");
if (!$stmt) {
return array("error"=>"true", "error_mysqli"=>$mysqli->error, "MORE"=>"INSERT INTO ".$table." (".$columns_text.") VALUES (".$values_text.")");
}
$stmt->bind_param($b_p_f, $values);
if ($stmt->execute()) {
return array("error"=>"false", "inserted_id"=>$mysqli->insert_id);
} else {
return array("error"=>"true", "error_stmt"=>$stmt->error, "MORE"=>"INSERT INTO ".$table." (".$columns_text.") VALUES (".$values_text.")");
}
}
Then I am calling to the function:
function hash_password($password) {
$options = [ 'cost' => 12 ];
return password_hash($password, PASSWORD_BCRYPT,$options);
}
$data = array(
"ID" => NULL,
"first_name" => "Alexander",
"last_name" => "Margolis",
"email" => "shay24590#gmail.com",
"username" => "smartDonkey",
"password" => "Incorrect",
"birthday" => "12-12",
"date_added" => time(),
"total_points" => 0,
"cafe_added" => 0,
"review_placed"=> 0);
$data["password"] = hash_password($data["password"]);
var_dump ( insert_to_db("user", $data, true) );
And I see on the screen
array(3) {
["error"]=> string(4) "true"
["error_stmt"]=> string(53) "No data supplied for parameters in prepared statement" ["MORE"]=> string(178) "..."
}
Why am I getting this? What is the problem?
Also, If I pass the value instead of ? to the mysql::prepare, it works! So - it means that the problem is with mysqli stmt bind_param..
I know that this question similar to others, but I didn't found one that helps my problem. and sorry for my english and for the long function. thank you!
I've moved to PDO, and instead of calling $stmt->bind_param($b_p_f, $values); you can call $pdo_stmt->execute($values) where $values is an Array.
I'm using this class to connect to database. It works just fine, except I couldn't get the lastInsertId().
<?php
class connDB
{
public function connDB()
{
require_once( 'dbconfig/config.php' );
$this->confPDO = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => false,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES UTF8"
);
try
{
$this->dbc = new PDO( "mysql:host=$this->dbHost;dbname=$this->dbName",
$this->dbUser, $this->dbPass, $this->confPDO );
}
catch( PDOException $errMsg )
{
return false;
}
}
public function exec( $sql, array $params = array() )
{
try
{
$this->stmt = $this->dbc->prepare( $sql );
if ( count( $params ) > 0 )
{
foreach ( $params as $k=>$v )
{
$this->bind($k, $v);
}
}
return $this->stmt->execute();
}
catch( PDOException $errMsg )
{
$this->dbc = null;
return false;
}
}
public function bind( $param, $value, $type = null )
{
if ( is_null( $type ) )
{
switch ( true )
{
// Boolen parameter
case is_bool( $value ):
$type = PDO::PARAM_BOOL;
break;
// Integer parameter
case is_int( $value ):
$type = PDO::PARAM_INT;
break;
// Null parameter
case is_null( $value ):
$type = PDO::PARAM_NULL;
break;
// String parameter
default:
$type = PDO::PARAM_STR;
}
}
$this->stmt->bindValue( $param, $value, $type );
}
public function single()
{
return $this->stmt->fetch(PDO::FETCH_ASSOC);
}
public function resultset()
{
return $this->stmt->fetchAll(PDO::FETCH_ASSOC);
}
public function rowCount()
{
return $this->stmt->rowCount();
}
}
Usage: [SELECT]
$sql = "SELECT * FROM < table >";
$db->exec($sql, $params);
$rows = $db->resultset();
foreach ($rows as $row)
{
echo $row['< column >'] . "\n";
}
Usage: [INSERT]
$sql = "INSERT INTO < table > (< column_1 >, < column_2 >, ... ) VALUES
(:valuename_1,
:valuename_2, ...)";
$params = array(':valuename_1' => 'value', ':valuename_2' => 'value', ...);
$db->exec($sql, $params);
I tried to do it this way:
include_once'classe.php';
$db = new connDB();
$sql = "INSERT INTO < table > (< column_1 >, < column_2 >, ... ) VALUES
(:valuename_1,
:valuename_2, ...)";
$params = array(':valuename_1' => 'value', ':valuename_2' => 'value', ...);
$db->exec($sql, $params);
$id = $db->lastInsertId();
I am getting an error:
Fatal error: Call to undefined method connDB::lastInsertId() in
I've tried adding a method into the class:
public function lastinsert()
{
// Return result
return $this->stmt->lastInsertId();
}
Then I called it like this this:
$db = new connDB();
$id = $db->lastinsert();
The error this time was
Fatal error: Call to undefined method PDOStatement::lastInsertId() in
There is no lastInsertId() method in your class.
You need to add it to the connDB class.
you need to call $dbc, not $stmt to get lastInsertId();
$this->dbc->lastInsertId();
as this function belongs to PDO class, not PDO statement class
Also, this piece of code may cause the problem
catch( PDOException $errMsg )
{
$this->dbc = null;
return false;
}
}
Make your exec() function this way
public function exec( $sql, array $params = array() )
{
$this->stmt = $this->dbc->prepare( $sql );
foreach ( $params as $k=>$v )
{
$this->bind($k, $v);
}
return $this->stmt->execute();
}
Using the same database class and was able to use lastInsertId like this:
$db = new connDB();
...
$_SESSION['users_id'] = $db->dbc->lastInsertId('id');
If you are using the model's save function:
$row->save();
You can use:
return $row->save();
which returns the id
I am trying to assemble a database insert with PDO through an array but am just missing it somewhere and am looking for some help on what I'm missing. The array is an associative array. Error thrown is:
Fatal error: Uncaught exception \'PDOException\' with message \'SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens\' in /var/www/html/themonastery.org/mot/receiver.php:70
Stack trace:
#0 /var/www/html/themonastery.org/mot/receiver.php(70): PDOStatement->execute()
#1 {main}
thrown in /var/www/html/themonastery.org/mot/receiver.php on line 70
Code I'm using is:
/** PDO Stuff **/
//require and instantiate pdo instance
require_once "dependancies/pdo.func.php";
$dbh = pdo_connect();
//implode query
$keys = implode(',', array_keys($clean));
$vals = implode(',', array_fill(0, count($clean), '?'));
$insert = array_values($clean);
//pdo prepare
$sth = $dbh->prepare("INSERT INTO backupDB ($keys) VALUES ($vals)");
//set loop condition
$waiting = true;
while($waiting) {
try {
$dbh->beginTransaction();
$i=1;
foreach($clean as $insert) {
// bindvalue is 1-indexed, so $k+1
$sth->bindValue($i++, $insert, PDO::PARAM_STR);
$sth->execute();
sleep(1);
}
$dbh->commit();
$waiting = false;
} catch(PDOException $e) {
if(stripos($e->getMessage(), 'DATABASE IS LOCKED') !== false) {
//sleep for 0.25 seconds and try again.
$dbh->commit();
usleep(250000);
} else {
$dbh->rollBack();
throw $e;
}
}
}
Here's the associative array,
array (
'full_name' => 'First Middle Last Suffix',
'first_name' => 'First',
'middle_name' => 'Middle',
'last_name' => 'Last Suffix',
'address' => 'The Address',
'city' => 'City',
'state' => 'State Abbr',
'zip' => 'Zip code',
'country' => 'Country Abbr',
'email' => 'dev#null.com',
'password' => 'd41d8cd98f00b204e9800998ecf8427e',
'ordinationDate' => '2012-04-15',
'birthday' => '1982-14-01',
'isValidAge' => '1',
)
And by request here's a var_dump of $keys and $vals
$keys = string(123) "full_name,first_name,middle_name,last_name,address,city,state,zip,country,email,password,ordinationDate,birthday,isValidAge"
$vals = string(27) "?,?,?,?,?,?,?,?,?,?,?,?,?,?"
Here's the column names from the DB
id full_name first_name middle_name last_name address city state zip country email password ordinationDate birthday isValidAge sex timestamp ulc_edit_time osc_sync guid
Change this:
$i=1;
foreach($clean as $insert) {
// bindvalue is 1-indexed, so $k+1
$sth->bindValue($i++, $insert, PDO::PARAM_STR);
$sth->execute();
sleep(1);
}
to this:
$i=1;
foreach($clean as $insert) {
// bindvalue is 1-indexed, so $k+1
$sth->bindValue($i++, $insert, PDO::PARAM_STR);
sleep(1);
}
$sth->execute();
PDO::execute() need to be at the end of all bindValues() (see http://php.net/manual/en/pdostatement.execute.php#example-995)
Additionaly, I have the follow function to bind correct data type (need some changes for your case):
public function bindValue($key = null, $value = null) {
if($key == null) {
return;
}
if(is_int($value)) {
$param = PDO::PARAM_INT;
} elseif(is_bool($value)) {
$param = PDO::PARAM_BOOL;
} elseif(is_null($value)) {
$param = PDO::PARAM_NULL;
} elseif(is_string($value)) {
$param = PDO::PARAM_STR;
} else {
$param = FALSE;
}
$this->_query->bindValue($key, $value, $param);
}