Zerofill is lost when using mysqli->prepare()
Asked Answered
U

3

1

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.

Unattended answered 26/7, 2011 at 4:1 Comment(0)
U
0

Another solution was to specify the field as decimal(6,0). For some reason it works as expected (no changes to the code required).

Unattended answered 26/7, 2011 at 5:4 Comment(1)
important note as this has been accepted as the right answer: this does NOT work on auto-increment fields such as ids.Sociolinguistics
W
3

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:

  1. CAST(intCol AS CHAR) casts the integer including the leading zeros to a string.
  2. 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.

Weepy answered 4/11, 2014 at 8:0 Comment(1)
PDO does it too if you set it to really use prepared statements, see #25010645Pipe
U
0

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.

Unattended answered 26/7, 2011 at 4:18 Comment(0)
U
0

Another solution was to specify the field as decimal(6,0). For some reason it works as expected (no changes to the code required).

Unattended answered 26/7, 2011 at 5:4 Comment(1)
important note as this has been accepted as the right answer: this does NOT work on auto-increment fields such as ids.Sociolinguistics

© 2022 - 2024 — McMap. All rights reserved.