I have a table field in a MySQL database:
userid INT(11)
So I am calling it to my page with this query:
"SELECT userid FROM DB WHERE name='john'"
Then for handling the result I do:
$row=$result->fetch_assoc();
$id=$row['userid'];
Now if I do:
echo gettype($id);
I get a string. Shouldn't this be an integer?
When you select data from a MySQL database using PHP the datatype will always be converted to a string. You can convert it back to an integer using the following code:
$id = (int) $row['userid'];
Or by using the function intval():
$id = intval($row['userid']);
Use the mysqlnd (native driver) for php.
If you're on Ubuntu:
sudo apt-get install php5-mysqlnd
sudo service apache2 restart
If you're on Centos:
sudo yum install php-mysqlnd
sudo service httpd restart
The native driver returns integer types appropriately.
As #Jeroen has pointed out, this method will only work out-of-the-box for PDO.
As #LarsMoelleken has pointed out, this method will work with mysqli if you also set the MYSQLI_OPT_INT_AND_FLOAT_NATIVE option to true.
Example:
$mysqli->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, TRUE);
Easiest Solution I found:
You can force json_encode to use actual numbers for values that look like numbers:
json_encode($data, JSON_NUMERIC_CHECK)
(since PHP 5.3.3).
Or you could just cast your ID to an int.
$row = $result->fetch_assoc();
$id = (int) $row['userid'];
My solution is to pass the query result $rs and get a assoc array of the casted data as the return:
function cast_query_results($rs) {
$fields = mysqli_fetch_fields($rs);
$data = array();
$types = array();
foreach($fields as $field) {
switch($field->type) {
case 3:
$types[$field->name] = 'int';
break;
case 4:
$types[$field->name] = 'float';
break;
default:
$types[$field->name] = 'string';
break;
}
}
while($row=mysqli_fetch_assoc($rs)) array_push($data,$row);
for($i=0;$i<count($data);$i++) {
foreach($types as $name => $type) {
settype($data[$i][$name], $type);
}
}
return $data;
}
Example usage:
$dbconn = mysqli_connect('localhost','user','passwd','tablename');
$rs = mysqli_query($dbconn, "SELECT * FROM Matches");
$matches = cast_query_results($rs);
// $matches is now a assoc array of rows properly casted to ints/floats/strings
No. Regardless of the data type defined in your tables, PHP's MySQL driver always serves row values as strings.
You need to cast your ID to an int.
$row = $result->fetch_assoc();
$id = (int) $row['userid'];
This happens when PDO::ATTR_EMULATE_PREPARES is set to true on the connection.
Careful though, setting it to false disallows the use of parameters more than once. I believe it also affects the quality of the error messages coming back.
I like Chad's answer, especially when the query results will be passed on to javascript in a browser. Javascript deals cleanly with numeric like entities as numbers but requires extra work to deal with numeric like entities as strings. i.e. must use parseInt or parseFloat on them.
Building on Chad's solution I use this and it is often exactly what I need and creates structures that can be JSON encoded for easy dealing with in javascript.
while ($row = $result->fetch_assoc()) {
// convert numeric looking things to numbers for javascript
foreach ($row as &$val) {
if (is_numeric($val))
$val = $val + 0;
}
}
Adding a numeric string to 0 produces a numeric type in PHP and correctly identifies the type so floating point numbers will not be truncated into integers.
$mysqli->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, TRUE);
Try this - worked for me.
For mysqlnd only:
mysqli_options($conn, MYSQLI_OPT_INT_AND_FLOAT_NATIVE, true);
Otherwise:
$row = $result->fetch_assoc();
while ($field = $result->fetch_field()) {
switch (true) {
case (preg_match('#^(float|double|decimal)#', $field->type)):
$row[$field->name] = (float)$row[$field->name];
break;
case (preg_match('#^(bit|(tiny|small|medium|big)?int)#', $field->type)):
$row[$field->name] = (int)$row[$field->name];
break;
default:
$row[$field->name] = $row[$field->name];
break;
}
}
If prepared statements are used, the type will be int where appropriate. This code returns an array of rows, where each row is an associative array. Like if fetch_assoc() was called for all rows, but with preserved type info.
function dbQuery($sql) {
global $mysqli;
$stmt = $mysqli->prepare($sql);
$stmt->execute();
$stmt->store_result();
$meta = $stmt->result_metadata();
$params = array();
$row = array();
while ($field = $meta->fetch_field()) {
$params[] = &$row[$field->name];
}
call_user_func_array(array($stmt, 'bind_result'), $params);
while ($stmt->fetch()) {
$tmp = array();
foreach ($row as $key => $val) {
$tmp[$key] = $val;
}
$ret[] = $tmp;
}
$meta->free();
$stmt->close();
return $ret;
}
In my project I usually use an external function that "filters" data retrieved with mysql_fetch_assoc.
You can rename fields in your table so that is intuitive to understand which data type is stored.
For example, you can add a special suffix to each numeric field:
if userid is an INT(11) you can rename it userid_i or if it is an UNSIGNED INT(11) you can rename userid_u.
At this point, you can write a simple PHP function that receive as input the associative array (retrieved with mysql_fetch_assoc), and apply casting to the "value" stored with those special "keys".
In my case mysqlnd.so extension had been installed. BUT i hadn't pdo_mysqlnd.so. So, the problem had been solved by replacing pdo_mysql.so with pdo_mysqlnd.so.
I like mastermind's technique, but the coding can be simpler:
function cast_query_results($result): array
{
if ($result === false)
return null;
$data = array();
$fields = $result->fetch_fields();
while ($row = $result->fetch_assoc()) {
foreach ($fields as $field) {
$fieldName = $field->name;
$fieldValue = $row[$fieldName];
if (!is_null($fieldValue))
switch ($field->type) {
case 3:
$row[$fieldName] = (int)$fieldValue;
break;
case 4:
$row[$fieldName] = (float)$fieldValue;
break;
// Add other type conversions as desired.
// Strings are already strings, so don't need to be touched.
}
}
array_push($data, $row);
}
return $data;
}
I also added checking for query returning false rather than a result-set.
And checking for a row with a field that has a null value.
And if the desired type is a string, I don't waste any time on it - its already a string.
I don't bother using this in most php code; I just rely on php's automatic type conversion. But if querying a lot of data, to then perform arithmetic computations, it is sensible to cast to the optimal types up front.
You can do this with...
mysql_fetch_field()
mysqli_result::fetch_field_direct or
PDOStatement::getColumnMeta()
...depending on the extension you want to use. The first is not recommended because the mysql extension is deprecated. The third is still experimental.
The comments at these hyperlinks do a good job of explaining how to set your type from a plain old string to its original type in the database.
Some frameworks also abstract this (CodeIgniter provides $this->db->field_data()).
You could also do guesswork--like looping through your resulting rows and using is_numeric() on each. Something like:
foreach($result as &$row){
foreach($row as &$value){
if(is_numeric($value)){
$value = (int) $value;
}
}
}
This would turn anything that looks like a number into one...definitely not perfect.
MySQL has drivers for many other languages, converting data to string "standardizes" data and leaves it up to the user to type-cast values to int or others
If you are using classes/objects to store your db data, you can type cast its attributes, so it would be converted to the right type:
<?php
class User
{
public int $id; // use type casting in class definition
}
$user1 = new User();
$user1->id = $row['userid'];
echo gettype($user1->id); // integer
?>
note: I would like to point out that Charlie solution above worked in my windows environment but changing the PDO::ATTR_EMULATE_PREPARES to false did not work when I tried to deploy my project on a linux server. I still got strings instead of numbers.
Related
I have a table field in a MySQL database:
userid INT(11)
So I am calling it to my page with this query:
"SELECT userid FROM DB WHERE name='john'"
Then for handling the result I do:
$row=$result->fetch_assoc();
$id=$row['userid'];
Now if I do:
echo gettype($id);
I get a string. Shouldn't this be an integer?
When you select data from a MySQL database using PHP the datatype will always be converted to a string. You can convert it back to an integer using the following code:
$id = (int) $row['userid'];
Or by using the function intval():
$id = intval($row['userid']);
Use the mysqlnd (native driver) for php.
If you're on Ubuntu:
sudo apt-get install php5-mysqlnd
sudo service apache2 restart
If you're on Centos:
sudo yum install php-mysqlnd
sudo service httpd restart
The native driver returns integer types appropriately.
As #Jeroen has pointed out, this method will only work out-of-the-box for PDO.
As #LarsMoelleken has pointed out, this method will work with mysqli if you also set the MYSQLI_OPT_INT_AND_FLOAT_NATIVE option to true.
Example:
$mysqli->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, TRUE);
Easiest Solution I found:
You can force json_encode to use actual numbers for values that look like numbers:
json_encode($data, JSON_NUMERIC_CHECK)
(since PHP 5.3.3).
Or you could just cast your ID to an int.
$row = $result->fetch_assoc();
$id = (int) $row['userid'];
My solution is to pass the query result $rs and get a assoc array of the casted data as the return:
function cast_query_results($rs) {
$fields = mysqli_fetch_fields($rs);
$data = array();
$types = array();
foreach($fields as $field) {
switch($field->type) {
case 3:
$types[$field->name] = 'int';
break;
case 4:
$types[$field->name] = 'float';
break;
default:
$types[$field->name] = 'string';
break;
}
}
while($row=mysqli_fetch_assoc($rs)) array_push($data,$row);
for($i=0;$i<count($data);$i++) {
foreach($types as $name => $type) {
settype($data[$i][$name], $type);
}
}
return $data;
}
Example usage:
$dbconn = mysqli_connect('localhost','user','passwd','tablename');
$rs = mysqli_query($dbconn, "SELECT * FROM Matches");
$matches = cast_query_results($rs);
// $matches is now a assoc array of rows properly casted to ints/floats/strings
No. Regardless of the data type defined in your tables, PHP's MySQL driver always serves row values as strings.
You need to cast your ID to an int.
$row = $result->fetch_assoc();
$id = (int) $row['userid'];
This happens when PDO::ATTR_EMULATE_PREPARES is set to true on the connection.
Careful though, setting it to false disallows the use of parameters more than once. I believe it also affects the quality of the error messages coming back.
I like Chad's answer, especially when the query results will be passed on to javascript in a browser. Javascript deals cleanly with numeric like entities as numbers but requires extra work to deal with numeric like entities as strings. i.e. must use parseInt or parseFloat on them.
Building on Chad's solution I use this and it is often exactly what I need and creates structures that can be JSON encoded for easy dealing with in javascript.
while ($row = $result->fetch_assoc()) {
// convert numeric looking things to numbers for javascript
foreach ($row as &$val) {
if (is_numeric($val))
$val = $val + 0;
}
}
Adding a numeric string to 0 produces a numeric type in PHP and correctly identifies the type so floating point numbers will not be truncated into integers.
$mysqli->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, TRUE);
Try this - worked for me.
For mysqlnd only:
mysqli_options($conn, MYSQLI_OPT_INT_AND_FLOAT_NATIVE, true);
Otherwise:
$row = $result->fetch_assoc();
while ($field = $result->fetch_field()) {
switch (true) {
case (preg_match('#^(float|double|decimal)#', $field->type)):
$row[$field->name] = (float)$row[$field->name];
break;
case (preg_match('#^(bit|(tiny|small|medium|big)?int)#', $field->type)):
$row[$field->name] = (int)$row[$field->name];
break;
default:
$row[$field->name] = $row[$field->name];
break;
}
}
If prepared statements are used, the type will be int where appropriate. This code returns an array of rows, where each row is an associative array. Like if fetch_assoc() was called for all rows, but with preserved type info.
function dbQuery($sql) {
global $mysqli;
$stmt = $mysqli->prepare($sql);
$stmt->execute();
$stmt->store_result();
$meta = $stmt->result_metadata();
$params = array();
$row = array();
while ($field = $meta->fetch_field()) {
$params[] = &$row[$field->name];
}
call_user_func_array(array($stmt, 'bind_result'), $params);
while ($stmt->fetch()) {
$tmp = array();
foreach ($row as $key => $val) {
$tmp[$key] = $val;
}
$ret[] = $tmp;
}
$meta->free();
$stmt->close();
return $ret;
}
In my project I usually use an external function that "filters" data retrieved with mysql_fetch_assoc.
You can rename fields in your table so that is intuitive to understand which data type is stored.
For example, you can add a special suffix to each numeric field:
if userid is an INT(11) you can rename it userid_i or if it is an UNSIGNED INT(11) you can rename userid_u.
At this point, you can write a simple PHP function that receive as input the associative array (retrieved with mysql_fetch_assoc), and apply casting to the "value" stored with those special "keys".
In my case mysqlnd.so extension had been installed. BUT i hadn't pdo_mysqlnd.so. So, the problem had been solved by replacing pdo_mysql.so with pdo_mysqlnd.so.
I like mastermind's technique, but the coding can be simpler:
function cast_query_results($result): array
{
if ($result === false)
return null;
$data = array();
$fields = $result->fetch_fields();
while ($row = $result->fetch_assoc()) {
foreach ($fields as $field) {
$fieldName = $field->name;
$fieldValue = $row[$fieldName];
if (!is_null($fieldValue))
switch ($field->type) {
case 3:
$row[$fieldName] = (int)$fieldValue;
break;
case 4:
$row[$fieldName] = (float)$fieldValue;
break;
// Add other type conversions as desired.
// Strings are already strings, so don't need to be touched.
}
}
array_push($data, $row);
}
return $data;
}
I also added checking for query returning false rather than a result-set.
And checking for a row with a field that has a null value.
And if the desired type is a string, I don't waste any time on it - its already a string.
I don't bother using this in most php code; I just rely on php's automatic type conversion. But if querying a lot of data, to then perform arithmetic computations, it is sensible to cast to the optimal types up front.
You can do this with...
mysql_fetch_field()
mysqli_result::fetch_field_direct or
PDOStatement::getColumnMeta()
...depending on the extension you want to use. The first is not recommended because the mysql extension is deprecated. The third is still experimental.
The comments at these hyperlinks do a good job of explaining how to set your type from a plain old string to its original type in the database.
Some frameworks also abstract this (CodeIgniter provides $this->db->field_data()).
You could also do guesswork--like looping through your resulting rows and using is_numeric() on each. Something like:
foreach($result as &$row){
foreach($row as &$value){
if(is_numeric($value)){
$value = (int) $value;
}
}
}
This would turn anything that looks like a number into one...definitely not perfect.
MySQL has drivers for many other languages, converting data to string "standardizes" data and leaves it up to the user to type-cast values to int or others
If you are using classes/objects to store your db data, you can type cast its attributes, so it would be converted to the right type:
<?php
class User
{
public int $id; // use type casting in class definition
}
$user1 = new User();
$user1->id = $row['userid'];
echo gettype($user1->id); // integer
?>
note: I would like to point out that Charlie solution above worked in my windows environment but changing the PDO::ATTR_EMULATE_PREPARES to false did not work when I tried to deploy my project on a linux server. I still got strings instead of numbers.
I am creating a custom class to make connecting to and using mysql easier in general. I have created many functions and all works fine except one part. I cannot seem to figure out how to determing if a string is an actual string or if it is a mysql function in order to put quotes around it or not. I put together a function that I can pass two parameters and it will insert into the database. The first parameter is the table to insert into, while the second parameter is an array of column, value pairs. Maybe this code example will help you understand a bit more. Note: I am escaping all strings already and have removed that code to show simplicity of problem.
function insert($table,array $array){
$table = "`".$table."`";
$columns = "";
$values = "";
foreach($array as $key => $value){
$columns = ($columns=="") ? "`".$key."`" : $columns.", `".$key."`";
if($value===NULL || $value=="NULL"){
$value = "NULL";
} else {
$value = "'".$value."'";
}
$values = ($values=="") ? "".$value."" : $values.", ".$value."";
}
$query = "INSERT INTO $table ($columns) VALUES ($values)";
return $this->query($query);
}
Here is an example of two different uses and the expected results or each
$data = array(
"first_name"=>"John",
"last_name"=>"Doe",
"entry_date"=>"2013-08-26"
);
$MyClass->insert("people",$data);
would execute
INSERT INTO `people` (`first_name`,`last_name`,`entry_date`) VALUES ('John','Doe','2013-08-26')
which is expected However the following use:
$data = array(
"first_name"=>"John",
"last_name"=>"Doe",
"entry_date"=>"NOW()"
);
$MyClass->insert("people",$data);
would execute as
INSERT INTO `people` (`first_name`,`last_name`,`entry_date`) VALUES ('John','Doe','NOW()')
which will not work because MySQL functions will not be operated if they are withing quotation marks. I know that this may be a small security risk because it would allow functions from the user if not caught. Is there a better way to do this or should MySQL functions not be allowed in an class insert function like this? If there is a regex that someone has put together to find and allow mysql functions then that might be a solution. If no such regex exists then one possibility would be to write one from scratch which I am not too good at...
EDIT:
Another solution which I will be using in the meantime is to get the date with PHP when forming the array. I would like to be using MySQL functions if possible still though. Here is my current code which solves the problem but not how I prefer:
$data = array(
"first_name"=>"John",
"last_name"=>"Doe",
"entry_date"=>date('Y-m-d H:i:s'),
);
$MyClass->insert("people",$data);
I implemented a feature to do what you're describing for Zend Framework 1.0 a few years ago.
I solved it by distinguishing between a plain string scalar and an object that contains a string.
$data = array(
"first_name"=>"John",
"last_name"=>"Doe",
"entry_date"=>new Zend_Db_Expr("NOW()")
);
$MyClass->insert("people",$data);
That way my framework can tell the difference between a PHP string that I intend to be treated
as an SQL string (i.e. quoted), versus a string that is rolled up in an object.
if($value===NULL || $value=="NULL"){
$value = "NULL";
} else if ($value instanceof Zend_Db_Expr) {
$value = $value->__toString(); // no quotes
} else {
$value = "'".$value."'";
}
PS: Some people may advise you to use query parameters instead of concatenating escaped variables as you are doing. I agree with that recommendation, but it really has little to do with your original question, so I won't go into it. There are many other StackOverflow questions that deal with using query parameters.
Re your comment:
You could use regexes to match NOW() and every other MySQL function and treat them specially, but how can you insert the literal string "NOW()" (not the result of the function of that name)?
Answer: you can't -- just like you can't insert the literal string "NULL" in your current code, because you have treated that word as a special case.
You need some way to distinguish between the SQL expression NOW() and "NOW()". Just because you don't need to insert that string into your database today doesn't mean you will never need that capability. Remember you're designing this insert() function for general use against any table, current or future.
You are in fact designing a framework.
Thanks #phpisuber1 for the regex if(preg_match("/^[A-Z_]+\(.*?\)/", $value)) { // Mysql function } and thanks #BillKarwin, with the two of you I have created a solution with a combination of your input.
The solution to me was add some more parameters to my classes function with default values to tell the function whether or not to insert as MySQL function or as string and also whether to insert as NULL or as string "NULL" see new function below:
/**
* #param string $table the table to insert array into
* #param array $array contains column => value pairs values may be MySQL Function
* #param bool $mysql_functions true to insert mysql functions as function, false to insert mysql functions as string
* #param bool $null_string_is_null true to insert string "NULL" as NULL; false to insert string "NULL" as string "NULL"
* #param bool $empty_string_is_null true to insert string "" as NULL; false to insert string "" as string ""
* #return bool Returns FALSE on failure. For successful queries will return TRUE.
*/
function insert($table,array $array,$mysql_functions = false,$null_string_is_null = true,$empty_string_is_null = false){
$table = "`".$table."`";
$columns = "";
$values = "";
foreach($array as $key => $value){
$columns = ($columns=="") ? "`".$key."`" : $columns.", `".$key."`";
if($value===NULL || ($value=="NULL" && $null_string_is_null) || ($value=="" && $empty_string_is_null)){
$value = "NULL";
} else if(preg_match("/^[A-Z_]+\(.*?\)/", $value) && $mysql_functions) {
$value = "".$value."";
} else {
$value = "'".$value."'";
}
$values = ($values=="") ? "".$value."" : $values.", ".$value."";
}
$query = "INSERT INTO $table ($columns) VALUES ($values)";
return $this->query($query);
}
This again is not the full function as I have left parts out for security reasons, but this is a functioning version. I will be looking into query parameters and see if it will increase security for this and other functions instead of concatenating escaped strings. The default parameters for this function work for my default use and can be changed for future uses. Overall I think this is the best solution. If you think otherwise I would love to read your comments.
Using:
MySQL 5.1 + PHP 5.3.5
MySQL:
id in "table" is defined as: mediumint(6) zerofill not null
I get the expected result when using:
$mysqli->query("SELECT id FROM table WHERE id = 1");
while($row = $ret->fetch_array(MYSQLI_ASSOC)) $arr[] = $row;
>>> $arr[0]["id"] = 000001
But not when I use prepared statement:
$ret = $mysqli->prepare("SELECT id FROM table WHERE id = ?");
call_user_func_array(array($ret,"bind_param"),array("i",1));
$ret->execute();
$ret->store_result();
$meta = $ret->result_metadata();
$fields = $meta->fetch_fields();
$cols = array(); $data = array();
foreach($fields as $field) $cols[] = &$data[$field->name];
call_user_func_array(array($ret, 'bind_result'), $cols);
while($ret->fetch()) {
$row = array();
foreach($data as $key => $val) $row[$key] = $val;
$arr[] = $row;
}
>>> $arr[0]["id"] = 1
When trying the prepared statement directly in the MySQL console, it shows as expected (it is not MySQL).
According to PHP mysqli_stmt::bind_result documentation I found this:
Depending on column types bound variables can silently change to the
corresponding PHP type.
I need to show the number with the trailing zeros WITHOUT having to do it in a later step. There are so many fields with zerofill and the process is practically automated from the data to the screen, so trying to fix this after this code, will require mayor changes.
The zerofill gets lost, because mysqli knows that the column is of type integer and so the type of the php variable becomes also int and so the leading zeros get lost.
This seems only to happen when you use prepared statements with mysqli (PDO seems not to have this behavior).
To prevent mysqli the type of the variable to integer you have to change the type in MySQL to something that is returned as a string.
There are two ways:
CAST(intCol AS CHAR) casts the integer including the leading zeros to a string.
Using the zerofilled integer in a string context, e.g. CONCAT(intCol, "").
Notice: If you use this value in an HTML5 input field of type number, this will also strip off leading zeros (at least in some browsers). To solve this problem have a look at this answer.
Temporally, I solved it adding these lines:
define("ZEROFILL_FLAG",64);
$zeros = array();
foreach($fields as $field) {
$zeros[$field->name] = $field->flags & ZEROFILL_FLAG ? $field->length : 0;
$cols[] = &$data[$field->name];
}
call_user_func_array(array($ret, 'bind_result'), $cols);
while($ret->fetch()) {
$row = array();
foreach($data as $key => $val) {
$row[$key] = $zeros[$key] > 0 ? sprintf('%0'.$zeros[$key].'s', $val) : $val;
}
$arr[] = $row;
}
Still I hope someone can show me a better way to solve it.
Another solution was to specify the field as decimal(6,0). For some reason it works as expected (no changes to the code required).
I have a table field in a MySQL database:
userid INT(11)
So I am calling it to my page with this query:
"SELECT userid FROM DB WHERE name='john'"
Then for handling the result I do:
$row=$result->fetch_assoc();
$id=$row['userid'];
Now if I do:
echo gettype($id);
I get a string. Shouldn't this be an integer?
When you select data from a MySQL database using PHP the datatype will always be converted to a string. You can convert it back to an integer using the following code:
$id = (int) $row['userid'];
Or by using the function intval():
$id = intval($row['userid']);
Use the mysqlnd (native driver) for php.
If you're on Ubuntu:
sudo apt-get install php5-mysqlnd
sudo service apache2 restart
If you're on Centos:
sudo yum install php-mysqlnd
sudo service httpd restart
The native driver returns integer types appropriately.
As #Jeroen has pointed out, this method will only work out-of-the-box for PDO.
As #LarsMoelleken has pointed out, this method will work with mysqli if you also set the MYSQLI_OPT_INT_AND_FLOAT_NATIVE option to true.
Example:
$mysqli->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, TRUE);
Easiest Solution I found:
You can force json_encode to use actual numbers for values that look like numbers:
json_encode($data, JSON_NUMERIC_CHECK)
(since PHP 5.3.3).
Or you could just cast your ID to an int.
$row = $result->fetch_assoc();
$id = (int) $row['userid'];
My solution is to pass the query result $rs and get a assoc array of the casted data as the return:
function cast_query_results($rs) {
$fields = mysqli_fetch_fields($rs);
$data = array();
$types = array();
foreach($fields as $field) {
switch($field->type) {
case 3:
$types[$field->name] = 'int';
break;
case 4:
$types[$field->name] = 'float';
break;
default:
$types[$field->name] = 'string';
break;
}
}
while($row=mysqli_fetch_assoc($rs)) array_push($data,$row);
for($i=0;$i<count($data);$i++) {
foreach($types as $name => $type) {
settype($data[$i][$name], $type);
}
}
return $data;
}
Example usage:
$dbconn = mysqli_connect('localhost','user','passwd','tablename');
$rs = mysqli_query($dbconn, "SELECT * FROM Matches");
$matches = cast_query_results($rs);
// $matches is now a assoc array of rows properly casted to ints/floats/strings
No. Regardless of the data type defined in your tables, PHP's MySQL driver always serves row values as strings.
You need to cast your ID to an int.
$row = $result->fetch_assoc();
$id = (int) $row['userid'];
This happens when PDO::ATTR_EMULATE_PREPARES is set to true on the connection.
Careful though, setting it to false disallows the use of parameters more than once. I believe it also affects the quality of the error messages coming back.
I like Chad's answer, especially when the query results will be passed on to javascript in a browser. Javascript deals cleanly with numeric like entities as numbers but requires extra work to deal with numeric like entities as strings. i.e. must use parseInt or parseFloat on them.
Building on Chad's solution I use this and it is often exactly what I need and creates structures that can be JSON encoded for easy dealing with in javascript.
while ($row = $result->fetch_assoc()) {
// convert numeric looking things to numbers for javascript
foreach ($row as &$val) {
if (is_numeric($val))
$val = $val + 0;
}
}
Adding a numeric string to 0 produces a numeric type in PHP and correctly identifies the type so floating point numbers will not be truncated into integers.
$mysqli->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, TRUE);
Try this - worked for me.
For mysqlnd only:
mysqli_options($conn, MYSQLI_OPT_INT_AND_FLOAT_NATIVE, true);
Otherwise:
$row = $result->fetch_assoc();
while ($field = $result->fetch_field()) {
switch (true) {
case (preg_match('#^(float|double|decimal)#', $field->type)):
$row[$field->name] = (float)$row[$field->name];
break;
case (preg_match('#^(bit|(tiny|small|medium|big)?int)#', $field->type)):
$row[$field->name] = (int)$row[$field->name];
break;
default:
$row[$field->name] = $row[$field->name];
break;
}
}
If prepared statements are used, the type will be int where appropriate. This code returns an array of rows, where each row is an associative array. Like if fetch_assoc() was called for all rows, but with preserved type info.
function dbQuery($sql) {
global $mysqli;
$stmt = $mysqli->prepare($sql);
$stmt->execute();
$stmt->store_result();
$meta = $stmt->result_metadata();
$params = array();
$row = array();
while ($field = $meta->fetch_field()) {
$params[] = &$row[$field->name];
}
call_user_func_array(array($stmt, 'bind_result'), $params);
while ($stmt->fetch()) {
$tmp = array();
foreach ($row as $key => $val) {
$tmp[$key] = $val;
}
$ret[] = $tmp;
}
$meta->free();
$stmt->close();
return $ret;
}
In my project I usually use an external function that "filters" data retrieved with mysql_fetch_assoc.
You can rename fields in your table so that is intuitive to understand which data type is stored.
For example, you can add a special suffix to each numeric field:
if userid is an INT(11) you can rename it userid_i or if it is an UNSIGNED INT(11) you can rename userid_u.
At this point, you can write a simple PHP function that receive as input the associative array (retrieved with mysql_fetch_assoc), and apply casting to the "value" stored with those special "keys".
In my case mysqlnd.so extension had been installed. BUT i hadn't pdo_mysqlnd.so. So, the problem had been solved by replacing pdo_mysql.so with pdo_mysqlnd.so.
I like mastermind's technique, but the coding can be simpler:
function cast_query_results($result): array
{
if ($result === false)
return null;
$data = array();
$fields = $result->fetch_fields();
while ($row = $result->fetch_assoc()) {
foreach ($fields as $field) {
$fieldName = $field->name;
$fieldValue = $row[$fieldName];
if (!is_null($fieldValue))
switch ($field->type) {
case 3:
$row[$fieldName] = (int)$fieldValue;
break;
case 4:
$row[$fieldName] = (float)$fieldValue;
break;
// Add other type conversions as desired.
// Strings are already strings, so don't need to be touched.
}
}
array_push($data, $row);
}
return $data;
}
I also added checking for query returning false rather than a result-set.
And checking for a row with a field that has a null value.
And if the desired type is a string, I don't waste any time on it - its already a string.
I don't bother using this in most php code; I just rely on php's automatic type conversion. But if querying a lot of data, to then perform arithmetic computations, it is sensible to cast to the optimal types up front.
You can do this with...
mysql_fetch_field()
mysqli_result::fetch_field_direct or
PDOStatement::getColumnMeta()
...depending on the extension you want to use. The first is not recommended because the mysql extension is deprecated. The third is still experimental.
The comments at these hyperlinks do a good job of explaining how to set your type from a plain old string to its original type in the database.
Some frameworks also abstract this (CodeIgniter provides $this->db->field_data()).
You could also do guesswork--like looping through your resulting rows and using is_numeric() on each. Something like:
foreach($result as &$row){
foreach($row as &$value){
if(is_numeric($value)){
$value = (int) $value;
}
}
}
This would turn anything that looks like a number into one...definitely not perfect.
MySQL has drivers for many other languages, converting data to string "standardizes" data and leaves it up to the user to type-cast values to int or others
If you are using classes/objects to store your db data, you can type cast its attributes, so it would be converted to the right type:
<?php
class User
{
public int $id; // use type casting in class definition
}
$user1 = new User();
$user1->id = $row['userid'];
echo gettype($user1->id); // integer
?>
note: I would like to point out that Charlie solution above worked in my windows environment but changing the PDO::ATTR_EMULATE_PREPARES to false did not work when I tried to deploy my project on a linux server. I still got strings instead of numbers.
When using mysql_fetch_assoc in PHP, how can I make it return the correct data types? Right now it appears to convert everything to strings, I'd prefer if it left the Ints as Ints, and somehow designated the Date/Time as either Object or somehow different than strings.
The reason for this is that I am using PHP as a backend to a Flex application, and Flex has some features such as automatically detecting return types, which don't work that well if everything comes in as a string.
I think a good strategy here is to programatically determine the datatype of each column in a table, and cast the returned results accordingly. This will allow you to interact with your database in a more consistent and simple manner while still giving you the control you need to have your variables storing the correct datatype.
One possible solution: You could use mysql_fetch_field() to get an object that holds meta-data about the table column and then cast your string back to the desired type.
//run query and get field information about the row in the table
$meta = mysql_fetch_field($result, $i);
//get the field type of the current column
$fieldType = $meta->type
A full example can be found here: http://us2.php.net/manual/en/function.mysql-fetch-field.php
Since PHP is loosely typed, you should have a relatively easy time with this.
If you are using OO (object-oriented) techniques, you could create a class with this functionality in the setter() methods so you don't have to have duplicate code.
Just contributing a small improvement to mastermind202's answer to handle more data types. Thanks mastermind for doing the heavy lifting!
function cast_query_results($rs) {
$fields = mysqli_fetch_fields($rs);
$data = array();
$types = array();
foreach($fields as $field) {
switch($field->type) {
case MYSQLI_TYPE_NULL:
$types[$field->name] = 'null';
break;
case MYSQLI_TYPE_BIT:
$types[$field->name] = 'boolean';
break;
case MYSQLI_TYPE_TINY:
case MYSQLI_TYPE_SHORT:
case MYSQLI_TYPE_LONG:
case MYSQLI_TYPE_INT24:
case MYSQLI_TYPE_LONGLONG:
$types[$field->name] = 'int';
break;
case MYSQLI_TYPE_FLOAT:
case MYSQLI_TYPE_DOUBLE:
$types[$field->name] = 'float';
break;
default:
$types[$field->name] = 'string';
break;
}
}
while($row=mysqli_fetch_assoc($rs)) array_push($data,$row);
for($i=0;$i<count($data);$i++) {
foreach($types as $name => $type) {
settype($data[$i][$name], $type);
}
}
return $data;
}
Example usage:
$db = mysqli_connect(...);
$rs = mysqli_query($db, "SELECT ...");
$results = cast_query_results($rs);
Returns an associative array of rows with properly typed fields
You could build a mysql-specific layer around mdb2 that automatically detects field types using the SHOW COLUMNS command, but that would kind of defeat the purpose of using mdb2.
Keep in mind, also, that mysql suports integers well outside of PHP's range, (UNSIGNED BIGINT is 64 bits; PHP supports, at best, 64 bit signed ints, and less on 32 bit platforms) so automatically casting may be undesirable in some contexts. In those cases, you really want to keep the large ints in their string form, and manipulate them with bcmath
I wanted to share a function I wrote for this same problem. Pass the query result $rs and get a assoc array of the casted data as the return:
function cast_query_results($rs) {
$fields = mysqli_fetch_fields($rs);
$data = array();
$types = array();
foreach($fields as $field) {
switch($field->type) {
case 3:
$types[$field->name] = 'int';
break;
case 4:
$types[$field->name] = 'float';
break;
default:
$types[$field->name] = 'string';
break;
}
}
while($row=mysqli_fetch_assoc($rs)) array_push($data,$row);
for($i=0;$i<count($data);$i++) {
foreach($types as $name => $type) {
settype($data[$i][$name], $type);
}
}
return $data;
}
Example usage:
$dbconn = mysqli_connect('localhost','user','passwd','tablename');
$rs = mysqli_query($dbconn, "SELECT * FROM Matches");
$matches = cast_query_results($rs);
// $matches is now a assoc array of rows properly casted to ints/floats/strings