I know this is an old question, but I wanted to do a little bit of digging in since I always forget the inner workings of the data type parameter when binding values/parameters. Also I have significantly more experience with MySQL, so this answer will be more MySQL-related.
MySQL automatically converts numbers to strings as necessary, and vice versa, so if you do:
SELECT * FROM tablename WHERE columnname = 42
or
SELECT * FROM tablename WHERE columnname = '42'
MySQL knows what column type columnname
should be and will automatically type cast if the wrong type is provided as the value.
Now, looking at the source code for PDOStatement:
if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_STR && param->max_value_len <= 0 && !Z_ISNULL_P(parameter)) {
if (Z_TYPE_P(parameter) == IS_DOUBLE) {
char *p;
int len = zend_spprintf_unchecked(&p, 0, "%.*H", (int) EG(precision), Z_DVAL_P(parameter));
ZVAL_STRINGL(parameter, p, len);
efree(p);
} else {
convert_to_string(parameter);
}
} else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_INT && (Z_TYPE_P(parameter) == IS_FALSE || Z_TYPE_P(parameter) == IS_TRUE)) {
convert_to_long(parameter);
} else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL && Z_TYPE_P(parameter) == IS_LONG) {
convert_to_boolean(parameter);
}
You'll notice from the above code that when the third parameter to the bind statements is either omitted or PDO_PARAM_STR
(default value), PHP will type cast the value to a string. When it is provided as PDO_PARAM_INT
, unlike what you may actually expect, it does not get type cast to an integer, but instead PHP casts it to a long! So if you do:
$stmt->bindValue("integer_column", "41.9", PDO_PARAM_INT);
The database will actually receive the long value 41.9
to be used in the query, which is then rounded (at least by MySQL) to the closest integer, so the final value used in the above query for a column of type INTEGER
will be 42
.
mysql> SELECT CAST(41.9 AS UNSIGNED);
+------------------------+
| CAST(41.9 AS UNSIGNED) |
+------------------------+
| 42 |
+------------------------+
The same is true if you have a non-numeric string, such as "41.9asdf"
:
$sth->bindValue("integer_column_value", "41.9asdf", PDO_PARAM_INT);
It will first be type cast to a long by PHP (41.9
), and then that value will be rounded to an integer by MySQL (42
).
Like I mentioned earlier, MySQL automatically converts strings to numbers, so if you give MySQL the string "123"
, it will have no problems converting this to an integer, or any other numeric type. However, keep in mind that when MySQL converts from a string to an integer, it truncates instead of rounds, so this could be an actual difference between providing the type when binding.
mysql> SELECT CAST('123.6' AS UNSIGNED);
+---------------------------+
| CAST('123.6' AS UNSIGNED) |
+---------------------------+
| 123 |
+---------------------------+
mysql> SELECT CAST(123.6 AS UNSIGNED);
+-------------------------+
| CAST(123.6 AS UNSIGNED) |
+-------------------------+
| 124 |
+-------------------------+
So here are the values an integer column will contain in PHP for the following:
$stmt->bindValue("integer_column", "123.6", PDO_PARAM_INT); // 124
$stmt->bindValue("integer_column", "123.6"); // 123
$stmt->bindValue("integer_column", (int) "123.6"); // 123
$stmt->bindValue("integer_column", round("123.6")); // 124
Note, as you may notice from the above, PHP casts a string to integer differently than how MySQL does. MySQL rounds floats, but PHP will use the floor
value:
var_dump((int) "123.6"); // int(123)
var_dump((int) 123.6); // int(123)
Now, to actually answer the security aspect of your question, there is no security benefit whatsoever in providing the third parameter to the prepared statements. Prepared statements are sufficient in and of themselves to mitigate SQL injection when done correctly (see this link for some obscure edge-cases).