PHP & MySQL: zerofill is lost when using mysqli->p

2019-08-24 11:26发布

问题:

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.

Any hint of how to solve this problem is welcome.

回答1:

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.



回答2:

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.



回答3:

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