Related
Trying to retrieve information from the database t_awhole and input it into a_whole. I've read up on a lot of these, but cannot find anything specific to my question.
The code stops where I have placed the comment //>>>>Stops here<<<<<. I used an echo statement to find that it does not pass this point.
This is a email confirmation function, so t_awhole's table structure is different than a_whole. Everything is the same but in t_awhole the first column is a confirmation code associated to to that user. Once they click the link in their email, the row with that confirmation code should transfer all the data from that t_awhole into a_whole. However, it should not transfer the confirmation code, but should add a new column for the number of the row (first column) as an increment, and whether the user is an admin or not (last column). Everything else will stay as it was (fN, lN, aI, eml, pss) in between that first and last row. Please tell me how to do this. Someone attempted below, but his answer was difficult to understand (although he tried and I thank him for that).
Finally, I am using PDO as the structure. It was originally written in mysql. I notice a colon : is used instead of a money sign $. How do I switch this to be from sql to PDO?
<?php
include('db.php');
// passkey that got from link
$pk=$_GET['pk'];
$t_awhole_conf="t_awhole";
// Retrieve data from table where row that match this passkey
$sql_conf1="SELECT * FROM $t_awhole_conf WHERE confirm_code ='$pk'";
$result_conf=mysql_query($sql_conf1) or die ('Error updating database: '.mysql_error());
// If successfully queried
if($result_conf){
// Count how many row has this passkey
$count=mysql_num_rows($result_conf);
// if found this passkey in our database, retrieve data from table "t_awhole"
if($count==1){
$rows=mysql_fetch_array($result_conf);
$fN = $rows['fN']; // capitalizes the first letter (6-26-14)
$lN = $rows['lN']; // capitalizes the first letter (6-26-14)
$aI = $rows['aI'];
$eml = $rows['eml'];
$pss = $rows['pss'];
$pss2 = $rows['pss2'];
$a_whole_conf="a_whole";
// Insert data that retrieves from "t_awhole" into table "a_whole"
$sql_conf2= $conn->prepare("INSERT INTO $a_whole_conf(fN, lN, aI, eml, pss, admin)
VALUES ($fN, $lN, $aI, $eml, $pss, $admin)");
//>>>>Stops here<<<<<
$result_conf2=$sql_conf2->execute() or die ('Error updating database: '.mysql_error());
}
// if not found passkey, display message "Wrong Confirmation code"
else {
echo "Wrong Confirmation code";
}
// if successfully moved data from table"t_awhole" to table "a_whole" displays message "Your account has been activated" and don't forget to delete confirmation code from table "t_awhole"
if($result_conf2){
echo "Your account has been activated";
// Delete information of this user from table "t_awholeb" that has this passkey
$sql_conf3="DELETE FROM $t_awhole_conf WHERE confirm_code = '$pk'";
$result_conf3=mysql_query($sql_conf3);
}
}
?>
TO ANSWER THE QUESTION YOU ASKED
The colon (:) is used in SQL text to identify a placeholder for a named bind parameter within a prepared statement. That colon gets included in the SQL text, and value for that placeholder is provided when the SQL statement is executed.
The "money sign" identifies a PHP variable; in the string context, the variable is evaluated, and the value of the variable gets incorporated into the SQL text.
The following is not an answer to the question you asked. But I think this will answer several other questions you should be asking...
The inclusion of "unsafe" values in PHP variables is where the "SQL Injection" vulnerability comes in, if we don't have any guarantee that the value of the variables don't contain some characters that will be interpreted as SQL. And that's exactly why the mysql interface includes the mysql_real_escape_string function. This is a "wrapper" that inspects values, and properly escapes values so that they will be seen as values, and not be interpreted as SQL syntax.
Q: 1. The code stops where I have placed the comment //>>>>Stops here<<<<<.
A: Cool. It's not clear how you know the code "Stops here", if you're getting some sort of error or what.
We don't see any declaration or assignment for the reference to the $admin variable. We do see that we expected column pss2 to be retrieved by the SELECT. But we don't see anything be done with that, except to assign that to a PHP variable named $pss2, and that doesn't appear to be referenced anywhere else. Curious.
Q: 2. This is a email confirmation function, so t_awhole's table structure is different than a_whole. Everything is the same but in t_awhole the first column is a confirmation code associated to to that user. Once they click the link in their email, the row with that confirmation code should transfer all the data from that t_awhole into a_whole. However, it should not transfer the confirmation code, but should add a new column for the number of the row (first column) as an increment, and whether the user is an admin or not (last column). Everything else will stay as it was (fN, lN, aI, eml, pss) in between that first and last row.
A: Seems like an awkward design. It's not clear why you need to loop through all the individual rows returned by a SELECT (and your code is subject to SQL injection. Hopefully, "Little Bobby Tables" doesn't register... http://xkcd.com/327/)
I'm not sure why you don't just run a single INSERT .. SELECT statement to "copy" the rows from one table to the other in one fell swoop, for example:
$sql = "INSERT INTO a_whole
( fN , lN, aI, eml, pss, admin)
SELECT t.fN, t.lN, t.aI, t.eml, t.pss, '' AS admin
FROM t_awhole t
WHERE t.confirm_code = '" . mysql_real_escape_string($pk) "'";
(I don't see any declaration or assignment to $admin in the original code, so I replaced that reference with a literal string (zero length) in the example above.)
If you were going to do this with PDO, you could use a prepared statement with a bind placeholder. All of the SQL is the same, with the exception that we replace a reference to the value of the PHP $pk variable with a bind placeholder in the SQL text:
$sql = "INSERT INTO a_whole
( fN , lN, aI, eml, pss, admin)
SELECT t.fN, t.lN, t.aI, t.eml, t.pss, '' AS admin
FROM t_awhole t
WHERE t.confirm_code = :pk";
Now the SQL text is a constant string, and is not subject to SQL injection.
With PDO, you'd first call the prepare(), and then call bind_param() and execute(), e.g.
$sth = $dbh->prepare($sql);
$sth->bindParam(':pk', $pk, PDO::PARAM_INT);
$sth->execute();
BUT... to do that, you need to have a PDO connection (referenced as $dbh above); you can't use a mysql connection with PDO.
(If you don't check the result from each call, you'd want to set the error handling on the connection to throw an error, and use a try/catch block to catch any PDOException.)
UPDATE: actually, I see that your code only copies the first row returned from the SELECT, we don't see a normal while (fetch) loop we usually see. That was my oversight there, seeing something I expected but that wasn't there. That's my bad. Still, there's no need to retrieve the values into PHP variables, if all we are going to do with them is insert them into another table. Let the database do that, without mucking up the code with a bunch of variables we don't need.
Q: 3. Finally, I am using PDO as the structure. It was originally written in mysql. I notice a colon : is used instead of a money sign $. Why is this and where would I switch the : for the $ in my code?
A: The code modified in the edit, is now calling functions both from the deprecated mysql interface; and PDO (per the references to PDO functions.)
It's not valid to mix mysql interface functions and PDO interface functions. You can't use PDO functions on a connection obtained using the mysql interface, etc.
This is likely why your code "stops here".
(I was a little confused earlier; I didn't see the PDO functions, all I saw was mysql functions. We're not used to seeing mysql and PDO functions mixed like this, mostly because we never see this because it's not valid.)
TO ANSWER THE QUESTION YOU ASKED
The colon (:) is used in SQL text to identify a placeholder for a named bind parameter within a prepared statement. That colon gets included in the SQL text, and value for that placeholder is provided when the SQL statement is executed.
The "money sign" identifies a PHP variable; in the string context, the variable is evaluated, and the value of the variable gets incorporated into the SQL text. (This is where the "SQL Injection vulnerability comes in... we don't have any guarantee that the value of that variable doesn't contain text that will be interpreted as SQL.
And that's exactly why the mysql interface includes the mysql_real_escape_string function.
//The variables are declared outside this function. I use $_POST to retrieve each user input in another .php file using html form tag. The variable $db is my database connection.
function insertintodb ($db, $avar, $bvar, $cvar)
{
/*
How can I tell what values my variables are when using PDO bindParam? I output $avar to see its value in this function. How can I tell if PDO actually binded ":firstname" to $avar? Likewise with the other variables.
*/
echo 'before <br>';
echo $avar;
echo '<br>';
//firstname, midinitial, lastname are values in my database.
//name is my table I am inserting into.
$insertname = "INSERT INTO name (firstname, midinitial, lastname)
VALUES (:firstname, :midname, :lastname)";
echo 'before PDO prepare<br>';
echo $avar;
echo '<br>';
$stmt = $db->prepare($insertname);
$stmt->bindParam(':firstname', $avar);
$stmt->bindParam(':midname', $bvar);
$stmt->bindParam(':lastname', $cvar);
echo 'after binding variables using bindParam <br>';
echo $avar;
echo '<br>';
$stmt->execute();
echo 'after executing <br>';
echo $avar;
echo '<br>';
}
bindParam() returns true or false:
if($stmt->bindParam(':firstname', $avar)) {
echo 'Woo hoo yay!';
} else {
echo 'Boo hoo waa';
}
Just avoid bindParam at all. this will relieve you from burden of checking its result
function insertintodb ($db, $avar, $bvar, $cvar)
$sql = "INSERT INTO name (firstname, midinitial, lastname) VALUES (?, ?, ?)";
$stmt = $db->prepare($sql);
$data = array_slice(func_get_args()); // lets strip $db from the func args
$stmt->execute($data);
}
Trust PDO
If PDO has a bug, this is not your problem. It is PDO's - since it's fairly well tested, there are very few bugs in current PDO versions. That is, if you tell PDO to bind, then trust that it will bind (PDO will fail on execute if there are unbound parameters, so we don't even have to "trust" it too much).
However, use PDOStatement::bindValue (not bindParam, except in special cases) because bindValue will ensure the value supplied is bound and not merely a "reference [to the variable]". This prevents "accidental changes" to variables between binding and execution from affecting the query.
Write and Test a DAL
Write a Data-Access Layer (DAL)1, as opposed to inline spaghetti SQL, and then test it. While ensuring the parameter is "actually binded" sounds useful, it isn't doesn't ensure the code is valid semantically. For instance, what if the code incorrectly did $stmt->bindParam(':firstname', $lastname);?
Furthermore, PDO itself will fail (I recommend enabling Exceptions) on most basic "binding failures" (such as unbound parameters or nonconvertible values) when the query is executed, making the very nature of testing if a parameter is "actually binded" less important.
Since detecting binding is not relevant to determining the validity of the code, nor can PDO report exactly what data is stored due SQL conversion issues (including truncation), then the problem really isn't about checking each bind, it's about checking operations - and a DAL provides guaranteed contracts for different operations.
1 A DAL doesn't have to be scary, nor does it have to use "ORM" or "OOP" or "DI/IOC" or anything else (although an advanced DAL may use all of those "concepts"). Consider, for starters, a small handful of functions in a separately included file which are the only mechanism for connecting to the database and "talking to" SQL.
Each of these functions then has a simple contract (which as documentation on the top) which defines the parameters it takes, the results it returns, and any exceptions it may throw.
Congratulations, you've created a minimal and testable DAL!
Then this DAL, which is just a collection of functions for now, can be taken and tested/verified outside of the "actual program" (preferably using an existing test framework/harness).
PDO always returns field values as strings when using MySQL. Is PDO consistent when using another database like MSSQL?
If not, is there a flag which forces PDO to always return strings (for purpose of consistency)? or better still to return native types for all values?
From what I can tell, Drupal makes it possible to use different databases using PDO. It performs the necessary conversions to make SQL statements compatible with the varying syntaxes. But how does it deal with data types in query results?
If you want to make sure that you always get strings you can use bindColumn() and specify the data type for each column
$sql = 'SELECT id, name FROM test';
$stmt = $dbh->query($sql);
/* Bind by column number */
$stmt->bindColumn(1, $id, PDO::PARAM_STR); //or PDO::PARAM_INT
$stmt->bindColumn(2, $name, PDO::PARAM_STR);
while ($row = $stmt->fetch(PDO::FETCH_BOUND)) {
var_dump($id); var_dump($name);
}
In so far as I remember, this depends on the DB engine.
Some time ago, PDO would return a t or f strings for boolean fields in Postgres, and I vaguely recall that it was returning a true or false boolean the last time I used it.
You can normalize results into native types after checking getColumnMeta():
http://us3.php.net/manual/en/pdostatement.getcolumnmeta.php
Doing so comes with a few strings attached, though. The warnings in the php manual are one. The inconsistent values returned from an engine to the next are another:
List of PHP native_type's for PDO getColumnMeta()
I've always done the simple connection of mysql_connect, mysql_pconnect:
$db = mysql_pconnect('*host*', '*user*', '*pass*');
if (!$db) {
echo("<strong>Error:</strong> Could not connect to the database!");
exit;
}
mysql_select_db('*database*');
While using this I've always used the simple method to escape any data before making a query, whether that be INSERT, SELECT, UPDATE or DELETE by using mysql_real_escape_string
$name = $_POST['name'];
$name = mysql_real_escape_string($name);
$sql = mysql_query("SELECT * FROM `users` WHERE (`name` = '$name')") or die(mysql_error());
Now I understand this is safe, to an extent!
It escapes dangerous characters; however, it is still vulnerable to other attacks which can contain safe characters but may be harmful to either displaying data or in some cases, modifying or deleting data maliciously.
So, I searched a little bit and found out about PDO, MySQLi and prepared statements. Yes, I may be late to the game but I've read many, many tutorials (tizag, W3C, blogs, Google searches) out there and not a single one has mentioned these. It seems very strange as to why, as just escaping user input really isn't secure and not good practice to say the least. Yes, I'm aware you could use Regex to tackle it, but still, I'm pretty sure that's not enough?
It is to my understanding that using PDO/prepared statements is a much safer way to store and retrieve data from a database when the variables are given by user input. The only trouble is, the switch over (especially after being very stuck in my ways/habits of previous coding) is a little difficult.
Right now I understand that to connect to my database using PDO I would use
$hostname = '*host*';
$username = '*user*';
$password = '*pass*';
$database = '*database*'
$dbh = new PDO("mysql:host=$hostname;dbname=$database", $username, $password);
if ($dbh) {
echo 'Connected to database';
} else {
echo 'Could not connect to database';
}
Now, function names are different so no longer will my mysql_query, mysql_fetch_array, mysql_num_rows etc work. So I'm having to read/remember a load of new ones, but this is where I'm getting confused.
If I wanted to insert data from say a sign up/registration form, how would I go about doing this, but mainly how would I go about it securely? I assume this is where prepared statements come in, but by using them does this eliminate the need to use something like mysql_real_escape_string? I know that mysql_real_escape_string requires you to be connected to a database via mysql_connect/mysql_pconnect so now we aren't using either won't this function just produce an error?
I've seen different ways to approach the PDO method too, for example, I've seen :variable and ? as what I think are known as place holders (sorry if that is wrong).
But I think this is roughly the idea of what should be done to fetch a user from a database
$user_id = $_GET['id']; // For example from a URL query string
$stmt = $dbh->prepare("SELECT * FROM `users` WHERE `id` = :user_id");
$stmt->bindParam(':user_id', $user_id, PDO::PARAM_INT);
But then I'm stuck on a couple things, if the variable wasn't a number and was a string of text, you have to given a length after PDO:PARAM_STR if I'm not mistaken. But how can you give a set length if you're not sure on the value given from user in-putted data, it can vary each time? Either way, as far as I know to display the data you then do
$stmt->execute();
$result = $stmt->fetchAll();
// Either
foreach($result as $row) {
echo $row['user_id'].'<br />';
echo $row['user_name'].'<br />';
echo $row['user_email'];
}
// Or
foreach($result as $row) {
$user_id = $row['user_id'];
$user_name = $row['user_name'];
$user_email = $row['user_email'];
}
echo("".$user_id."<br />".$user_name."<br />".$user_email."");
Now, is this all safe?
If I am right, would inserting data be the same for example:
$username = $_POST['username'];
$email = $_POST['email'];
$stmt = $dbh->prepare("INSERT INTO `users` (username, email)
VALUES (:username, :email)");
$stmt->bindParam(':username, $username, PDO::PARAM_STR, ?_LENGTH_?);
$stmt->bindParam(':email, $email, PDO::PARAM_STR, ?_LENGTH_?);
$stmt->execute();
Would that work, and is that safe too? If it is right what value would I put in for the ?_LENGTH_?? Have I got this all completely wrong?
UPDATE
The replies I've had so far have been extremely helpful, can't thank you guys enough! Everyone has got a +1 for opening my eyes up to something a little different. It's difficult to choose the top answer, but I think Col. Shrapnel deserves it as everything is pretty much covered, even going into other arrays with custom libraries which I wasn't aware of!
But thanks to all of you:)
Thanks for the interesting question. Here you go:
It escapes dangerous characters,
Your concept is utterly wrong.
In fact "dangerous characters" is a myth, there are none.
And mysql_real_escape_string escaping but merely a string delimiters. From this definition you can conclude it's limitations - it works only for strings.
however, it is still vulnerable to other attacks which can contain safe characters but may be harmful to either displaying data or in some cases, modifying or deleting data maliciously.
You're mixing here everything.
Speaking of database,
for the strings it is NOT vulnerable. As long as your strings being quoted and escaped, they cannot "modify or delete data maliciously".*
for the other data typedata - yes, it's useless. But not because it is somewhat "unsafe" but just because of improper use.
As for the displaying data, I suppose it is offtopic in the PDO related question, as PDO has nothing to do with displaying data either.
escaping user input
^^^ Another delusion to be noted!
a user input has absolutely nothing to do with escaping. As you can learn from the former definition, you have to escape strings, not whatever "user input". So, again:
you have escape strings, no matter of their source
it is useless to escape other types of data, no matter of the source.
Got the point?
Now, I hope you understand the limitations of escaping as well as the "dangerous characters" misconception.
It is to my understanding that using PDO/prepared statements is a much safer
Not really.
In fact, there are four different query parts which we can add to it dynamically:
a string
a number
an identifier
a syntax keyword.
so, you can see that escaping covers only one issue. (but of course, if you treat numbers as strings (putting them in quotes), when applicable, you can make them safe as well)
while prepared statements cover - ugh - whole 2 isues! A big deal ;-)
For the other 2 issues see my earlier answer, In PHP when submitting strings to the database should I take care of illegal characters using htmlspecialchars() or use a regular expression?
Now, function names are different so no longer will my mysql_query, mysql_fetch_array, mysql_num_rows etc work.
That is another, grave delusion of PHP users, a natural disaster, a catastrophe:
Even when utilizing old mysql driver, one should never use bare API functions in their code! One have to put them in some library function for the everyday usage! (Not as a some magic rite but just to make the code shorter, less repetitive, error-proof, more consistent and readable).
The same goes for the PDO as well!
Now on with your question again.
but by using them does this eliminate the need to use something like mysql_real_escape_string?
YES.
But I think this is roughly the idea of what should be done to fetch a user from a database
Not to fetch, but to add a whatever data to the query!
you have to given a length after PDO:PARAM_STR if I'm not mistaken
You can, but you don't have to.
Now, is this all safe?
In terms of database safety there are just no weak spots in this code. Nothing to secure here.
for the displaying security - just search this site for the XSS keyword.
Hope I shed some light on the matter.
BTW, for the long inserts you can make some use of the function I wrote someday, Insert/update helper function using PDO
However, I am not using prepared statements at the moment, as I prefer my home-brewed placeholders over them, utilizing a library I mentioned above. So, to counter the code posted by the riha below, it would be as short as these 2 lines:
$sql = 'SELECT * FROM `users` WHERE `name`=?s AND `type`=?s AND `active`=?i';
$data = $db->getRow($sql,$_GET['name'],'admin',1);
But of course you can have the same code using prepared statements as well.
* (yes I am aware of the Schiflett's scaring tales)
I never bother with bindParam() or param types or lengths.
I just pass an array of parameter values to execute(), like this:
$stmt = $dbh->prepare("SELECT * FROM `users` WHERE `id` = :user_id");
$stmt->execute( array(':user_id' => $user_id) );
$stmt = $dbh->prepare("INSERT INTO `users` (username, email)
VALUES (:username, :email)");
$stmt->execute( array(':username'=>$username, ':email'=>$email) );
This is just as effective, and easier to code.
You may also be interested in my presentation SQL Injection Myths and Fallacies, or my book SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming.
Yes, :something is a named placeholder in PDO, ? is an anonymous placeholder. They allow you to either bind values one by one or all at once.
So, basically that makes four options to provide your query with values.
One by one with bindValue()
This binds a concrete value to your placeholder as soon as you call it. You may even bind hard coded strings like bindValue(':something', 'foo') if desired.
Providing a parameter type is optional (but suggested). However, since the default is PDO::PARAM_STR, you only need to specify it when it is not a string. Also, PDO will take care of the length here - there is no length parameter.
$sql = '
SELECT *
FROM `users`
WHERE
`name` LIKE :name
AND `type` = :type
AND `active` = :active
';
$stm = $db->prepare($sql);
$stm->bindValue(':name', $_GET['name']); // PDO::PARAM_STR is the default and can be omitted.
$stm->bindValue(':type', 'admin'); // This is not possible with bindParam().
$stm->bindValue(':active', 1, PDO::PARAM_INT);
$stm->execute();
...
I usually prefer this approach. I find it the cleanest and most flexible.
One by one with bindParam()
A variable is bound to your placeholder that will be read when the query is executed, NOT when bindParam() is called. That may or may not be what you want. It comes in handy when you want to repeatedly execute your query with different values.
$sql = 'SELECT * FROM `users` WHERE `id` = :id';
$stm = $db->prepare($sql);
$id = 0;
$stm->bindParam(':id', $id, PDO::PARAM_INT);
$userids = array(2, 7, 8, 9, 10);
foreach ($userids as $userid) {
$id = $userid;
$stm->execute();
...
}
You only prepare and bind once which safes CPU cycles. :)
All at once with named placeholders
You just drop in an array to execute(). Each key is a named placeholder in your query (see Bill Karwins answer). The order of the array is not important.
On a side note: With this approach you cannot provide PDO with data type hints (PDO::PARAM_INT etc.). AFAIK, PDO tries to guess.
All at once with anonymous placeholders
You also drop in an array to execute(), but it is numerically indexed (has no string keys). The values will replace your anonymous placeholders one by one in the order they appear in your query/array - first array value replaces first placeholder and so forth. See erm410's answer.
As with the array and named placeholders, you cannot provide data type hints.
What they have in common
All of those require you to bind/provide as much values as you have
placeholders. If you bind too many/few, PDO will eat your children.
You don't have to take care about escaping, PDO handles that. Prepared PDO statements are SQL injection safe by design. However, that's not true for exec() and query() - you should generally only use those two for hardcoded queries.
Also be aware that PDO throws exceptions. Those could reveal potentially sensitive information to the user. You should at least put your initial PDO setup in a try/catch block!
If you don't want it to throw Exceptions later on, you can set the error mode to warning.
try {
$db = new PDO(...);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING)
} catch (PDOException $e) {
echo 'Oops, something went wrong with the database connection.';
}
To answer the length question, specifying it is optional unless the param you are binding is an OUT parameter from a stored procedure, so in most cases you can safely omit it.
As far as safety goes, escaping is done behind the scenes when you bind the parameters. This is possible because you had to create a database connection when you created the object. You are also protected from SQL injection attacks since by preparing the statement, you are telling your database the format of the statement before user input can get anywhere near to it. An example:
$id = '1; MALICIOUS second STATEMENT';
mysql_query("SELECT * FROM `users` WHERE `id` = $id"); /* selects user with id 1
and the executes the
malicious second statement */
$stmt = $pdo->prepare("SELECT * FROM `users` WHERE `id` = ?") /* Tells DB to expect a
single statement with
a single parameter */
$stmt->execute(array($id)); /* selects user with id '1; MALICIOUS second
STATEMENT' i.e. returns empty set. */
Thus, in terms of safety, your examples above seem fine.
Finally, I agree that binding parameters individually is tedious and is just as effectively done with an array passed to PDOStatement->execute() (see http://www.php.net/manual/en/pdostatement.execute.php).
My page sends using ajax data to a php script. one of the vars that the script gets is: $product_disc
now a small test that I have done inside the script clearly revealed that $product_disc is null. the test:
if ($product_disc == null)
{$test="null";}
else {$test="not null";}
I make my code return $test to the ajax calling procedure:
$return_array['success'] = array ('test' => $test);
And using Firebug I see that the ajax response has: "test":"null"
So i conclude that $product_disc is of value: null
Now, inside my script I'm using the following code to insert data to MySql DB:
$stmt = mysqli_prepare($link, "INSERT INTO set_products (id,discount) VALUES (NULL , ?)");
mysqli_stmt_bind_param($stmt, "i", $product_disc);
mysqli_stmt_execute($stmt);
mysqli_stmt_close($stmt);
The query is being executed, but the value that was inserted is 0 and not NULL!
Only when I explicitly added: $product_disc=null; before the MySqli statements, the value that was inserted was NULL.
Why is that? What's the different between a null and a null? I followed this answer in stackoverflow hoping that i could easily insert NULLs but then came this problem...
Thank you!
PS #stackoverflow team - Your spell checking dictionary doesn't recognize the word stackoverflow!
You prepare product_disc as integer, null is not an integer. It is converted to 0.
While preparing as a string, would definitely fix your issue, it defeats the purpose of preparing a mysql statement. Consider this solution:
mysqli_stmt_bind_param($stmt, $product_disc==null?'s':'i', $product_disc);