I'm attempting to be more secure and start using PDO and prepared statements. This had been recommended to me and I've read up on these two websites:
http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/
and
http://webdevrefinery.com/forums/topic/1272-your-mysql-code-sucks
I've hit a brick wall and I can't understand why the following doesn't work. I am trying to insert a row (to log a 404 error). I have read up about named and unnamed placeholders and I think the named placeholder method will be easier to maintain. I've also tried using "try" and "catch" for the first time. All of this is completely new to me, so please be kind! I don't get any errors but the code doesn't update the database- I get zero rows returned.
Here is the code:
$referer = $_SERVER['HTTP_REFERER'];
$domainName = "http://domain.com";
$_dtNow = date("d-m-Y H:i:s");
$_referer = $domainName.$_SERVER['REQUEST_URI'];
$_thisPage = $domainName.$url;
$_ip = $_SERVER['REMOTE_ADDR'];
$_host = $_SERVER['REMOTE_HOST'];
if(isset($_SERVER['HTTP_USER_AGENT'])) {$_ua = $_SERVER['HTTP_USER_AGENT'];} else {$_ua = "unset";}
$host = 'localhost';
$port = 3306; // This is the default port for MySQL
$database = 'databaseName';
$username = 'username';
$password = 'password';
// Construct the DSN, or "Data Source Name". Really, it's just a fancy name
// for a string that says what type of server we're connecting to, and how
// to connect to it. As long as the above is filled out, this line is all
// you need :)
$dsn = "mysql:host=$host;port=$port;dbname=$database";
try {
// Connect!
$db = new PDO($dsn, $username, $password);
$data = array(
'dateTime' => $_dtNow,
'referer' => $_referer,
'page' => $_thisPage,
'ip' => $_ip,
'host' => $_host,
'ua' => $_ua
);
$statement = $db->prepare("INSERT INTO 404s (dateTime, referer, page, ip, host, ua) value (:dateTime, :referer, :page, :ip, :host, :ua)");
$statement->execute($data);
}
catch(PDOException $e) {
echo $e->getMessage();
}
?>
Are you sure of the table name is 404s it sound like an incorrect identifier.
INSERT INTO 404s (dateTime, referer, page, ip, host, ua) value (:dateTime, :referer, :page, :ip, :host, :ua)
Try :
INSERT INTO `404s` (dateTime, referer, page, ip, host, ua) value (:dateTime, :referer, :page, :ip, :host, :ua)
Use backquote around `404s`
Note
Keep in mind that construct such as :
create table `404` ( `33` integer);
Are valid .
When you build complex request use of ` is very useful to avoid some kind of painful SQL errors especially when you format request from an introspection algorithm.
Like table, columns and database have to be protected.
In addition to #Dave Kiss, the SQL statement has a small typo at 'value'. It should be 'VALUES'.
INSERT INTO 404s (dateTime, referer, page, ip, host, ua) VALUES (:dateTime, :referer, :page, :ip, :host, :ua)
Maybe that is all.
BTW: Using leading numbers in identifiers (like your table name 404s) is bad style. You may use them, but you have to but the table name in backticks:
INSERT INTO `404s` (dateTime, referer, page, ip, host, ua) VALUES (:dateTime, :referer, :page, :ip, :host, :ua)
Resources:
http://dev.mysql.com/doc/refman/5.6/en/insert.html
http://dev.mysql.com/doc/refman/5.6/en/identifiers.html
Try adding the colons to your keys in the data array.
/* Execute a prepared statement by passing an array of insert values */
$data = array(
':dateTime' => $_dtNow,
':referer' => $_referer,
':page' => $_thisPage,
':ip' => $_ip,
':host' => $_host,
':ua' => $_ua
);
$statement = $db->prepare("INSERT INTO 404s (dateTime, referer, page, ip, host, ua) value (:dateTime, :referer, :page, :ip, :host, :ua)");
$statement->execute($data);
Related
so i have a very weird issue that its been quite sometime now and am still figuring out why it is is. i have a function in sql server called fn_md5 that accepts 2 parameters and then return it as a varbinary.
but if the function is called via a the php script in my web, the hash is different when you directly call it from the ssms, and all of my ideas are out anymore. all i know is that the fn_md5 function when called directly in the ssms outputs the correct hash that i want it to be.
soo this is my fn_md5 function:
USE [master]
GO
/****** Object: UserDefinedFunction [dbo].[UFN_MD5_ENCODEVALUE] Script Date: 5/17/2022 4:41:16 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- ЗФјцён : UFN_MD5_ENCODEVALUE()
-- і»їл : ЖЇБ¤№®АЪї°ъ АОµ¦Ѕєё¦ АМїлЗПї© MD5 °ЄА» »эјє
ALTER FUNCTION [dbo].[fn_md5]
(
#btInStr VARCHAR(10),
#btInStrIndex VARCHAR(10)
)
RETURNS VARBINARY(16)
AS
BEGIN
DECLARE #btOutVal VARBINARY(16)
EXEC master..XP_MD5_EncodeKeyVal #btInStr, #btInStrIndex, #btOutVal OUT
RETURN #btOutVal
END
and this is my php script:
$xodbc_connect = odbc_connect('Driver={ODBC Driver 17 for SQL Server};SERVER=WIN-6QBM0ALD4G1\SQLEXPRESS', 'sa', 'superpasszz');
$statement = "select [dbo].fn_md5('passpass', 'useruser')";
$exec = odbc_exec($xodbc_connect, $statement);
$result = odbc_result($exec, 1);
$password = $result;
//var_dump($password);
# insert data
$data = array(
'username' => $username,
'password' => $password,
'name' => $username,
'serial' => $this->_defaultAccountSerial,
'email' => $email
);
# query
$query = "INSERT INTO "._TBL_MI_." ("._CLMN_USERNM_.", "._CLMN_PASSWD_.", "._CLMN_MEMBNAME_.", "._CLMN_SNONUMBER_.", "._CLMN_EMAIL_.", "._CLMN_BLOCCODE_.", "._CLMN_CTLCODE_.") VALUES (:username, :password, :name, :serial, :email, 0, 0)";
now if i do this script in the ssms:
select [dbo].fn_md5('passpass', 'useruser');
it will give me this, which should be the correct hash:
0x0634DF7B99E2CF2344C3362F8CB76729
but if i use the registration process via my web it will give me this instead:
0xE5E892FA86C13BB1DF93630458E7DC31
the more weirder is that i cannot see what is wrongg and my head is going to blow. am still constantly learning both languages please bare with me and i hope you can help me out :<
I am currently working on a project but can't continue due a error that keeps on coming and I don't know why but perhaps you guys would know. When I fill in my form and want to insert my data in the database I get the error feedback account creation failed. Which is below.
so here is the code:
// write new users data into database
$sql = "INSERT INTO users (user_name, user_password_hash, user_email, user_creation_timestamp, user_activation_hash, user_provider_type, user_persnaam, user_bondsnummer, user_telefoonnummer, user_leeftijd, user_enkelsterkte, user_dubbelsterkte, user_geslacht)
VALUES (:user_name, :user_password_hash, :user_email, :user_creation_timestamp, :user_activation_hash, :user_provider_type, :user_persnaam, :user_bondsnummer, :user_telefoonnummer, :user_leeftijd, :user_dubbelsterkte, :user_enkelsterkte, :user_geslacht)";
$query = $this->db->prepare($sql);
$query->execute(array(':user_name' => $user_name,
':user_password_hash' => $user_password_hash,
':user_email' => $user_email,
':user_persnaam' => $user_persnaam,
':user_bondsnummer' => $user_bondsnummer,
":user_telefoonnummer" => $user_telefoonnummer,
":user_enkelsterkte" => $user_enkelsterkte,
":user_dubbelsterkte" => $user_dubbelsterkte,
":user_leeftijd" => $user_leeftijd,
':user_geslacht' => $user_geslacht,
':user_creation_timestamp' => $user_creation_timestamp,
':user_activation_hash' => $user_activation_hash,
':user_provider_type' => 'DEFAULT'));
$count = $query->rowCount();
if ($count != 1) {
$_SESSION["feedback_negative"][] = FEEDBACK_ACCOUNT_CREATION_FAILED;
return false;
}
the problem seems to be that I can't write anything into my database but I am sure that I can connect and update everything but not write. So the problem isn't with the connection to the database. I think it has something to do with the rowcount but maybe you guys know what's wrong. The double quotes aren't the problem I've checked that already.
Thanks in advance
I don't know if this will be helpful, #fred gave you a few clue, and you said you are sure the connection is good. Here is what I caught:
INSERT INTO users
(user_name,
user_password_hash,
user_email,
user_creation_timestamp,
user_activation_hash,
user_provider_type,
user_persnaam,
user_bondsnummer,
user_telefoonnummer,
user_leeftijd,
user_enkelsterkte, /* here user_enkelsterkte is first */
user_dubbelsterkte, /* here user_dubbelsterkte is second */
user_geslacht)
VALUES (:user_name,
:user_password_hash,
:user_email,
:user_creation_timestamp,
:user_activation_hash,
:user_provider_type,
:user_persnaam,
:user_bondsnummer,
:user_telefoonnummer,
:user_leeftijd,
:user_dubbelsterkte, /* here user_dubbelsterkte is first */
:user_enkelsterkte, /* here user_enkelsterkte is second */
:user_geslacht)
If there is a column type or length mismatch it will fail.
Here is a better way to write your code would be:
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
if ($query = $this->db->prepare($sql)) {
// put code here
}else{
echo "\nPDO::errorInfo():\n";
print_r($this->db->errorInfo());
}
You can do error checking on connection, prepare() or execute().
Now you should find out if you get an error.
I have a set of data stored in variables, and I want a page to write that data to a MySQL database, I'd like to include the time of insertion, here's my method:
$username="username"; $password="password";
try {
$pdo = new PDO('mysql:host=localhost; dbname=db01', $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare('INSERT INTO table01
(
Time,
variable1,
variable2,
)
VALUES
(
:Time,
:variable1,
:variable2,
)');
$stmt->execute(array(
':Time' => NOW(),
':variable1' => $var1,
':variable2' => $var2,
));
echo $stmt->rowCount(); // 1
} catch(PDOException $e) {
echo 'Error: ' . $e->getMessage();
}
On loading this page, the css is stripped from the page, I get a sort of garbled output of what the page should look like (white screen, plain text some images), additionally on checking the database nothing has been written to it.
Without the Time variable in there, all works perfectly.
Any advice? Thanks in advance
Sorry, took me moment to re-read that. You are using the nysql function now() to do the work, and as such you don't need to set it as a param and therefore don't need to bind it at all. Just write it into your query.
$stmt = $pdo->prepare('INSERT INTO table01
(
Time,
variable1,
variable2,
)
VALUES
(
now(),
:variable1,
:variable2,
)');
$stmt->execute(array(
':variable1' => $var1,
':variable2' => $var2,
));
Edit Re Comment
The : in a query denotes it as a parameter in a prepared query. That means you must then bind it via the bind() command. However if you are inserting the data from within the database (such as using a built in function, or pulling the data in from another row) you don't need to declare it in the initial query with : and therefore you can't pass it to the query in the bound params array.
One from here:
$sth->execute(array(':calories' => $calories, ':colour' => $colour));
The other from here:
/*** reassign the variables again ***/
$data = array('animal_id'=>4, 'animal_name' => 'bruce');
/*** execute the prepared statement ***/
$stmt->execute($data);
My question is: :key or key ?
Sorry I don't have the PHP environment here.
Both are valid, but you are encouraged to use the :key notation, as it can prevent some errors, if you name a variable with a reserved keyword, for instance...
I would definitely trust php.net more than phpro.org ;)
However, in the same php.net page:
An array of insert values (named parameters) don't need the prefixed colon als key-value to work.
Both ways are correct and both examples run correct.
You can read about that in manual (user comments): http://www.php.net/manual/en/pdostatement.execute.php#71929
I use the second way, i.e.,
$query = 'insert into user (username, password) values (:username, :password)';
$param = array('username' => $username, 'password' => $password);
$param for this example sometimes can be made as compact('username', 'password') - seems handy to me.
Thanks for looking. All helpful answers/comments are up voted.
In php, you can use NOW() like this:
mysql_query("INSERT INTO tablename (id, value, time_created)
VALUES ('{$id}', '{$value}', NOW())");
How can I do the same thing in PDO. When I bind like this, I get an error:
$stmt->bindParam(':time_added', NOW(), PDO::PARAM_STR);
Is it the PDO:PARAM_STR?
Because nobody has explicitly answered the question, I'll add the correct answer for the sake of completeness.
$stmt = $pdoDb->prepare('INSERT INTO tablename (id, value, time_created) VALUES (:id, :value, NOW())');
// either bind each parameter explicitly
$stmt->bindParam(':id', $id); // PDOStatement::bindValue() is also possibly
$stmt->bindParam(':value', $value);
$stmt->execute();
// or bind when executing the statement
$stmt->execute(array(
':id' => $id,
':value' => $value
));
Presuming your PDO statement is correct you could do something like this:
$date = date('Y-m-d H:i:s');
$stmt->bindParam(':time_added', $date, PDO::PARAM_STR);
None of the answers solve the question as I see it!
So there are some of my findings:
there is NO WAY how to force PDO to pass MySQL function call as a query value - so there is no way to do simple wrapper that will be able to use NOW() or any other function as passed values. Every time you need something like that, you need manually change the query, so the function call is part of the query string. :-(
I'm using function that tests given values for MySQL function I am using and modifies the query itself, but it is not a good solution to my opinion... :-}
This might be useful to some of you, maybe not. I was confronted with the same problem as Ollie Saunders was. I'm pretty new to php/mysql, and most of all PDO. I was able to solve the problem with the following:
$active = 0;
$id = NULL;
$query = "INSERT
INTO tbl_user(ID_user, firstname, lastname, email, password, active, create_date)
VALUES (?,?,?,?,?,?,NOW())";
if($stmt=$this->conn->prepare($query)) {
$stmt->bind_param('issssi', $id, $firstname, $lastname, $email, $password, $active);
$stmt->execute();
}
and guess what it works! Hope to have helped here. Any comments are welcome. Try it and tell me if it worked for you, or if you have any additions.
To answer Elmo's question, you can create a PDO wrapper that allows for SQL functions like NOW(). You just need to pass an additional argument with the columns that you want to use SQL functions for. Here's mine:
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())
other than NOW() i also utilize the "timestamp" type column and set its default to CURRENT_TIMESTAMP .. so i just pass nothing for that field and time is automatically set. maybe not exactly what ur looking for.