Zerofill is lost when using mysqli->prepare() - php

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).

Related

PHPMyAdmin - Column Defined As Int, Shows Up As String [duplicate]

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.

why all my data become string even though originally it is an Integer in My database? [duplicate]

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.

Determine if key values in two arrays are equal then update if they are

I've taken up PHP programming as a hobby after work and kids. So, my appologies if this code is bogus...but I've been searching for awhile. Anyways, I am trying to compare the values in two arrays and...where each key value equals another I would like to update that value to '1'. So for example, in one array the values are (2, 4, 1) in the second they are (3, 4, 5). Afterwards the the values that would be in my sql column would be updated to (2, 1, 1). Here is my code. Thank you for any help!
$array1 = mysql_fetch_array($x);
$array2 = mysql_fetch_array($x1);
foreach($array1 as $key => $value)
{
if($value == $array2[$key])
{
$v = "UPDATE fXnumber SET xnumber = '1'";
mysql_query($v);
}
}
Check when you update table set id for identifier which record will be updated
$array1 = mysql_fetch_array($x);
$array2 = mysql_fetch_array($x1);
foreach($array1 as $key => $value)
{
if (isset($array2[$key]) && $value == $array2[$key])
{
$v = "UPDATE fXnumber SET xnumber = 1 WHERE id = '{$key}'";
mysql_query($v);
}
}
This is a version of your code with enhanced reasability. And I added an (currently invalid) 'WHERE' clause. I assume your sql statement in the version you suggesteed did not only update a single value in the database table as expected and desired, but _all values inside that table. most likely you want to specify which entry to update:
$array1 = mysql_fetch_array($x);
$array2 = mysql_fetch_array($x1);
foreach(array_keys($array1) as $key)
{
if ( $array1[$key]==$array2[$key] )
{
$v = sprintf("UPDATE fXnumber SET xnumber=1 WHERE id=%s", $key);
mysql_query($v);
}
}
Another hint: phps old mysql extension is depreciated. It has lots of issues. Consider using the newer mysqli extension or, even better, use the PDO abstraction layer. It protects you from code injection and the like.
For array comparing array_diff might help.

MySQL integer field is returned as string in PHP

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.

My database query keeps setting all columns to the same value in MySQL

Here is my update method for Kohana 3.
public function update($type, $id, $updates) {
$info = $this->getInfo($type);
$dbTable = $info['table'];
$updatesKeysToValues = array();
var_dump($updates);
foreach($updates as $key => $value) {
// if the value is null or the key isnt set for this, don't update!
if ($value === null OR ! isset($info['columnsToUpdateData'][$key])) continue;
$updatesKeyToValues[] = "`$key` = :$key";
}
$updatesKeyToValues = implode(', ', $updatesKeyToValues);
$query = 'UPDATE `' . $dbTable . '` SET ' . $updatesKeyToValues . ' WHERE id = :id LIMIT 1' ;
echo $query;
$dbQuery = DB::query(Database::UPDATE, $query);
foreach($updates as $key => $value) {
echo "$key === $value\n<br>";
$dbQuery->bind(':' . $key, $value);
}
$success = $dbQuery->bind(':id', $id)
->execute();
var_dump($success);
}
During every var_dump() and echo the data is fine. There is nothing to suggest why this is happening.
Essentially what I am doing is getting the data for this table from the config, building a query string with named params, looping and defining the named params and then executing. Instead of working, I end up with all fields the same (seems to be whatever was the last array value).
I can't seem to figure it out, can you? Many thanks for your time.
UPDATE
I just had a thought, are underscores valid in param names in a query?
ANOTHER UPDATE
Here is the output of the echo $query
UPDATE `personnel` SET `first_name` = :first_name, `last_name` = :last_name, `email` = :email WHERE id = :id LIMIT 1
I also cooked up that method of binding multiple params to the query too. I've never done it in a loop before, but I assumed it would work. In Kohana 2.x, I'd always used $bindings[] = 'tom#jones.com' etc, but the new Kohana doesn't accept an array as far as I can tell.
FINAL UPDATE
Thanks everyone, I think it is being passed by reference. I got around it by setting it to $updates[$key]
Looks like I could of also used the param() method instead of bind. View source
the bind function is using a reference the your $value
public function bind($param, & $var)
{
// Bind a value to a variable
$this->_parameters[$param] =& $var;
return $this;
}
Something that seems to work in a test
$a = array("a"=>1, "b"=>2, "c"=>3, "d"=>4, "e"=>5, "f"=>6);
$v = array();
$t = array();
$i = 0;
foreach($a as $key => $value)
{
$t[] = $key;
$v[] = &$t[$i];
$i++;
}
print_r($v);
results are here:
http://www.antiyes.com/test/hmm.php
do you think the $key & $value in
$dbQuery->bind(':' . $key, $value);
are being passed by reference ?
below didnt work
this line
$updatesKeyToValues[] = "`$key` = :$key";
could you change it to:
$updatesKeyToValues[] = "`" . $key ."` = " . ":" . $key;
and see what happens?
I don't know what data access layer you're using here, but I'm guessing this:
foreach($updates as $key => $value) {
$dbQuery->bind(':' . $key, $value);
}
could be doing something really deceptive: taking the parameters by reference.
So what would happen would be that since $value is a real variable, the bind() function receives a reference to it and remembers that it's this variable — not the variable's current value — that it will bind to the given parameter. Then you go the next time round the foreach loop, and we've got the classic loop problem of C-like languages: you're not getting a new instance of $key and $value, you're actually changing the existing variables you already had, just like for the standard for ($i= 0... loop.
So, when it comes time to do the query, what's parameter :a? It's the current value of $value, the last one in the loop. What's parameter :b? The same. And so on.
I know some PHP parameterisation interfaces do this (mysqli, I think?), but in general receiving parameters by reference is IMO extremely likely to lead to unwanted behaviours like this, and I certainly consider it totally inappropriate for a parameter-binding interface like this.
ETA: just looked at query.php in the link you posted to John's comment. Yes. Sigh. It's taking parameters by reference. How awful.
Why don't you use the query-builder?
This is just a quick guess, as I haven't had enough time to play with the query-builder myself.
$query = DB::update();
$query->set($updates);
// etc
Check out the source and I'm sure you could figure out how the query-builder works :)

Categories