153

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?

Donald Duck
  • 8,409
  • 22
  • 75
  • 99
luca
  • 36,606
  • 27
  • 86
  • 125
  • 2
    the MySQL query gives row values not row types or any other associated information. Just the raw data that's in each table cell. You'll have to account for this in PHP – Dan Hanly Mar 16 '11 at 09:24
  • If you need the column types, see here http://forums.whirlpool.net.au/archive/526795 – RichardTheKiwi Mar 16 '11 at 09:36
  • For readers, selected answer involves iterating through the results. But there is a better solution. http://stackoverflow.com/a/1197424/5079380 – Amr Eladawy Jul 03 '16 at 15:13
  • 2
    @DanHanly - technically, the MYSQL query (in PHP) returns *a string representation* of the cell value - the actual cell value stored in the database for a 32-bit integer is ... a 32-bit integer, not a string of digits. – ToolmakerSteve Apr 07 '19 at 17:59
  • for me, when i used `$conn->query("your query")` i got the integer fields as string but when i used `$conn->prepare("your query")` i got the parameters as they were in the database – Bar Tzadok Apr 22 '20 at 11:51

17 Answers17

145

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']);
Michiel Pater
  • 22,377
  • 5
  • 43
  • 57
  • 94
    Having come to MySQL from a Postgres background I feel the need to say that this is a huge pain in the arse. When you fetched a row in Postgres you coudl be sure that the elements in the row array had appropriate data types. Now I'm having to write code to manually cast every element to the data type I'm expecting. – GordonM Mar 25 '11 at 10:14
  • 29
    I just did some tests on Windows with Laravel and Mysql on the very same schema and database server. On Windows the primary key is returned as an Integer and on Linux it's a String. – AturSams Oct 28 '14 at 15:38
  • Ok it's a good idea but what if you have thousands of fields to retrieve ? This process take a while.. In my exemple I retrieve number of views for each post, then I print all my post in a table, I allow user to sort by View asc and desc, but it sort by "1" to "9" or "9" to "1" so in first place you could have "9999", "91", "8111", "7", etc... – KeizerBridge Feb 25 '15 at 10:07
  • @zehelvion have you ever found a solution to this? – Felipe Francisco Mar 10 '16 at 17:20
  • @FelipeFrancisco Did not find a solution. Knowing what to expect, allows to adapt your code for the production environment. – AturSams Mar 11 '16 at 15:35
  • 3
    Every field returned will be a string **OR NULL**. – Liam Jun 22 '16 at 20:02
  • 1
    So why am I getting int values for int columns (I'm using prepared statements and tested on windows) ? ! – Accountant م Feb 17 '17 at 01:04
  • Fix all the results with this is not affordable for big application. Solution should be something in the Mysqli connection option – Max Cuttins Mar 28 '18 at 12:12
  • @GordonM Not true. For example booleans are returned as the string "t" or "f" and an integer column with the value 3 will be returned as the string "3". Perhaps you were using a different library in PHP (such as PDO) rather than the builtin pg_ functions? – faintsignal Apr 10 '18 at 18:47
  • @faintsignal Given that it was 7 years ago I don't remember! – GordonM Apr 11 '18 at 11:36
  • This is not a correct answer. And with modern PHP installations (which use mysqlnd by default) there is a setting for this. $mysqlidb->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, true); – timeSmith Aug 21 '19 at 22:21
  • This is best answer: https://stackoverflow.com/a/50195153/2802965 – Hossein Oct 25 '19 at 12:30
  • This is apparently very different than `bind_result` and `fetch`... because the row returned from my mysqli is returning mixed (string), (int), (float), and NULL. Which is wrecking havoc on code that is explicitly looking for `=== '0'` and the like. Need to find a way to FORCE (string) always (opposite of OP). – IncredibleHat Aug 07 '20 at 20:23
  • @AturSams you have made my day! windows/MacOS you will get `int` in `integer` but when I deploy my code to `Cpanel` it shows me casting error that may be by linux hosting. – Mohsin AR Feb 17 '21 at 20:33
91

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);
Dharman
  • 30,962
  • 25
  • 85
  • 135
advait
  • 6,355
  • 4
  • 29
  • 39
  • I do not see any proof of this stament. Running with mysqlnd and mysqli, clearly string values are returned. – Jeroen Dec 09 '14 at 07:49
  • @Jeroen are you sure? There's plenty of documentation available. http://stackoverflow.com/questions/1197005/how-to-get-numeric-types-from-mysql-using-pdo – advait Dec 09 '14 at 18:04
  • yes. Also look at the comments below. Did you test this yourself? as it looks now, even mysqlnd only returns appropriate types if you use prepared statements. example: `$link = mysqli_connect('localhost', 'root', ''); mysqli_set_charset($link,'utf8'); $result = mysqli_query($link,'SELECT CAST(\'3.51\' AS DECIMAL(3,2))'); $row = mysqli_fetch_assoc($result); var_dump($row);` returns: `array(1) { ["CAST('3.51' AS DECIMAL(3,2))"]=> string(4) "3.51" }` – Jeroen Dec 10 '14 at 08:59
  • Ah, I didn't realize you were using mysqli. The native driver will only give you types for PDO. – advait Dec 10 '14 at 21:36
  • 1
    Not how I understand it, it should work equally with PDO and Mysqli. Even on the link you post above in the comment it is clearly stated that it ONLY WORKS WITH PREPARED STATEMENTS and not inline queries as per the OP. quote from that page: _But this is PHP 5.3 only (provided your version of PHP 5.3 is compiled with mysqlnd (and not old libmysql)), and seems to only be the case for prepared statements :-(_ – Jeroen Dec 11 '14 at 19:42
  • This solution will work but if you are using the phpmyadmin in linux it will not work. To use both mysqlnd and phpmyadmin check [here](http://blog.ahughes.org/?p=602). – Alankar More May 05 '15 at 06:25
  • I just test it out, after install php5-mysqlnd really works on Ubuntu. – Adriano Rosa Sep 17 '15 at 05:22
  • I was looking for a solution that wasn't involving me getting in the middle of the framework's ORM, and my application code, and this is exactly what I've been looking for. Thanks a lot! – parrker9 Apr 14 '16 at 03:55
  • Its fixed the issue. – Alizain Prasla May 13 '16 at 08:34
  • Had this issue when comparing my local Laravel environment to my Ubuntu Laravel environment. My "active" field was a tinyint but was returned on the server as a string. So when the `if($active==1) {//pass}` worked on my local environment and not the server, I was stumped. Used the answer marked correct just to be sure it worked, then installed this and tried it again and it worked great. Thanks to @advait and @Jeroen for figuring this out! – Ryan Ginnow May 16 '16 at 15:25
  • 8
    you can also use "MYSQLI_OPT_INT_AND_FLOAT_NATIVE" for mysqli – Lars Moelleken Jul 11 '16 at 02:22
  • I have confirmed that INTs and FLOATs are returned as PHP numbers with mysqli when php-mysqlnd is installed and active, **AND** MYSQLI_OPT_INT_AND_FLOAT_NATIVE is set to TRUE (ex: `$mysqli = mysqli_init(); $mysqli->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, TRUE);` – Timothy Zorn Jan 15 '18 at 07:26
  • @TimothyZorn - on what OS? – ToolmakerSteve Apr 07 '19 at 18:18
  • **Procedural Method:** `mysqli_options($conn, MYSQLI_OPT_INT_AND_FLOAT_NATIVE, TRUE);`. **Thanks**, it really works – Shubham Gupta Oct 03 '20 at 15:12
26

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'];
Larney
  • 1,674
  • 4
  • 23
  • 47
  • 2
    Using a `json_encode` to cast string to int if possible is like using microscope to choke nails. – LibertyPaul Aug 06 '17 at 17:37
  • 10
    This will break all the postalcodes and telephone number. Good way to introduce Bugs in your App. The solution MUST respect the mysql column datatype, not try to guess from the value. – Max Cuttins Mar 28 '18 at 12:07
  • 1
    Max Cuttins, your point is valid. However, if you can account for all the data types expected from the DB, this would then be a perfect route to take. – Ifedi Okonkwo Apr 17 '18 at 16:39
  • 1
    This will hurt you when you sometime have ids of some kind like '06' . This flag will convert it to 6. Which is wrong because those ids should kept trailing zeros. – Hassan Dad Khan Jun 14 '18 at 13:39
  • This is a great solution for values created by `UNIX_TIMESTAMP()`, for example. – David Jul 15 '20 at 17:22
  • Loved it because I need it for ID - Primary Key only. – Pratik Butani Sep 18 '20 at 08:49
  • This is perfect for my issue since I only care about some Booleans I'm passing to Javascript. Excellent solution, when used judiciously. – Rex Schrader Feb 23 '21 at 15:46
19

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
mastermind202
  • 407
  • 4
  • 5
  • 2
    Great solution except 'NULL' is also a variable type used by the settype() function. So your code changes all NULL values returned by the database (values where gettype() == 'NULL') into 'string' type with '' value, 'integer' type with 0 value, or 'float' type with 0.0 value. You need to not call settype if is_null($data[$i][$name]) is true. – Winter Dragoness Jun 08 '18 at 03:57
  • I like mastermind's technique, but the [coding can be simpler](https://stackoverflow.com/a/55563447/199364). – ToolmakerSteve Apr 07 '19 at 20:54
11

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'];
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • 7
    It's not PHP that's responsible (directly) for this behaviour, it's the database driver. If you're interfacing with a Postgres database from PHP you'll get appropriate data types back. – GordonM Mar 25 '11 at 10:15
6

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.

Charlie
  • 8,530
  • 2
  • 55
  • 53
5

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.

4
$mysqli->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, TRUE);

Try this - worked for me.

PLASMA chicken
  • 2,777
  • 2
  • 15
  • 25
3

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;
    }
  }
tim
  • 2,530
  • 3
  • 26
  • 45
2

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;
}
Fredrik
  • 587
  • 4
  • 7
2

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.

Milo
  • 3,365
  • 9
  • 30
  • 44
Anton
  • 21
  • 2
2

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.

ToolmakerSteve
  • 18,547
  • 14
  • 94
  • 196
2

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

d-_-b
  • 21,536
  • 40
  • 150
  • 256
Didax
  • 43
  • 5
1

You can do this with...

  1. mysql_fetch_field()
  2. mysqli_result::fetch_field_direct or
  3. 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.

Chad Hedgcock
  • 11,125
  • 3
  • 36
  • 44
0

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

wolfgang
  • 7,281
  • 12
  • 44
  • 72
  • 2
    I believe primitive types are pretty much a "standard"? If a language-specific driver couldn't fill the needs for that language, I would say it is very disappointing – Chung Jul 18 '16 at 04:09
0

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.

0

Since PHP 8.1 you're able to retrieve integers and floats with the according datatype instead of beeing converted to a string.

Integers and floats in result sets will now be returned using native PHP types instead of strings when using emulated prepared statements. This matches the behavior of native prepared statements. The previous behaviour can be restored by enabling the PDO::ATTR_STRINGIFY_FETCHES option.

https://www.php.net/manual/en/migration81.incompatible.php#migration81.incompatible.pdo.mysql