如何在PHP中将MySQL中的整数和数字列作为整数和数字返回?

96
问题在于PHP中的整型列(DB query)应该返回整数数据类型,但实际上每一列都被作为字符串类型返回。
我已经确保 "PDO::ATTR_STRINGIFY_FETCHES" 的值为false,以确保结果不会被强制转化为字符串。
我看到的答案有:
1. 无法解决。 - 不是真的,Mac OS X上安装的PHP/MySQL运行正常。 2. 在代码中将所有值进行类型转换。 - 我不会这样做。 3. 不用担心,PHP是弱类型语言。 - 我的数据会以JSON格式输出,并被许多其他服务使用,一些服务要求数据以正确的格式输出。
从我的研究中我了解到这是驱动程序实现问题。许多来源声称MySQL原生驱动程序不支持返回数字类型。但在Mac OS X上正常工作,除非他们想说“Linux上的MySQL本地驱动程序不支持此功能”。这意味着我在Mac OS X上安装的驱动程序/环境特殊。我一直试图识别差异以修复问题,但我的知识受限,不知道如何检查这些问题。
两个环境之间的区别包括: - OS X上的PHP是通过Home Brew编译和安装的。 - Ubuntu上的PHP是通过“apt-get install php5-dev”安装的。 - OS X上的PHP连接到运行在OS X上的MySQL服务器。 - Ubuntu上的PHP连接到Rackspace云数据库。
Ubuntu环境: - 版本:10.04.1 - PHP 5.4.21-1+debphp.org~lucid+1 (cli) (built: Oct 21 2013 08:14:37) - pdo_mysql已启用
Mac OS X环境: - 10.7.5 - PHP 5.4.16 (cli) (built: Aug 22 2013 09:05:58) - pdo_mysql已启用,其客户端API版本为mysqlnd 5.0.10。
使用的PDO标志:无。
PDO::ATTR_CASE => PDO::CASE_NATURAL,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::ATTR_EMULATE_PREPARES => false,

2
这是mysqlnd的责任。我相信。 - Your Common Sense
我有完全相同的问题,但是针对的是MS SQL Server。是否有类似于mysqlnd的强类型库?我能找到的最新驱动程序返回所有字符串。 - GSerg
3个回答

133

解决方法是确保您在php中使用 mysqlnd 驱动程序。

如何知道您没有使用mysqlnd?

查看 php -i,如果没有提到 "mysqlnd",则表明您没有使用它。而 pdo_mysql 部分会有类似于下面的内容:

pdo_mysql

PDO Driver for MySQL => enabled Client API version => 5.1.72

如何安装它?

L/A/M/P的大多数安装指南建议使用apt-get install php5-mysql,但是MySQL的原生驱动程序由不同的软件包安装:php5-mysqlnd。我发现这可以通过ppa:ondrej/php5-oldstable获得。

要切换到新驱动程序(在Ubuntu上):

  • 删除旧驱动程序:
    apt-get remove php5-mysql
  • 安装新驱动程序:
    apt-get install php5-mysqlnd
  • 重新启动apache2:
    service apache2 restart

如何检查正在使用的驱动程序?

现在,php -i将在pdo_mysql部分明确提到"mysqlnd":

pdo_mysql

PDO Driver for MySQL => enabled
Client API version => mysqlnd 5.0.10 - 20111026 - $Id:      e707c415db32080b3752b232487a435ee0372157 $

PDO设置

确保PDO::ATTR_EMULATE_PREPARESfalse(检查您的默认设置或设置它):
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

确保PDO::ATTR_STRINGIFY_FETCHESfalse(检查您的默认设置或设置它):
$pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);

返回值

  • 浮点类型(FLOAT,DOUBLE)将作为PHP浮点数返回。
  • 整数类型(INTEGER,INT,SMALLINT,TINYINT,MEDIUMINT,BIGINT †)将作为PHP整数返回。
  • 定点类型(DECIMAL,NUMERIC)将作为字符串返回。

† 大于64位有符号整数(9223372036854775807)的BIGINT将作为字符串返回(在32位系统上为32位)

    object(stdClass)[915]
      public 'integer_col' => int 1
      public 'double_col' => float 1.55
      public 'float_col' => float 1.5
      public 'decimal_col' => string '1.20' (length=4)
      public 'bigint_col' => string '18446744073709551615' (length=20)

3
请注意,即使启用这个版本的mysqlnd,DECIMALS仍将被发送为字符串...(尽管FLOATS将作为浮点数返回!) :/ 来源:我进行了测试。 - Pere
5
请注意,PDO::ATTR_STRINGIFY_FETCHES与此无关-我尝试过,对结果没有影响,并且在这篇文章中,用户@Josh Davis说它与MySQL无关。 - Pere
1
谢谢,我已经进一步调查并更新了关于DECIMAL类型的注释。 - stephenfrank
6
@pere,这是正确的行为。DECIMAL 不是数字类型,它是数字的字符串格式。因此,您可以返回 "0.30" 而不是 0.3。Decimal 就是用于此目的...所以这是正确的行为。 - Max Cuttins
为什么这是一个该死的妥协? 我需要PDO::ATTR_EMULATE_PREPARES来多次使用命名参数。 - Gaby_64

5
这个被接受的答案可行,似乎是谷歌上这个问题最受欢迎的答案。我的问题是我需要将一个应用程序部署到多个环境中,而且我不总是能够安装正确的驱动程序,此外我需要小数点为数字,而不是字符串。所以我创建了一个例程,在JSON编码之前进行类型转换,可以轻松修改以适应需求。这有点像从轨道上核弹攻击。
首先,使用“show columns from”从表中获取列。mysql query "SHOW COLUMNS FROM table like 'colmunname'":questions
$query = 'SHOW COLUMNS FROM ' . $table; //run with mysqli or PDO 

将类型放入数组中,按列名索引以便轻松迭代。假设从show columns返回的结果集存在名为$columns_from_table的变量中。http://php.net/manual/zh/function.array-column.php

$column_types = array_column( $columns_from_table, 'Type', 'Field');

下一步,我们要从类型中删除括号,例如varchar(32)或decimal(14,6)

foreach( $column_types as $col=>$type )
{
    $len = strpos( $type, '(' );
    if( $len !== false )
    {
        $column_types[ $col ] = substr( $type, 0, $len );
    }
}

现在我们有一个关联数组,其中列名作为索引,格式化的类型作为值,例如:
Array
(
    [id] => int
    [name] => varchar
    [balance] => decimal
    ...
)

现在,当您从表中进行选择时,您可以遍历结果并将值转换为适当的类型:

foreach( $results as $index=>$row )
{
    foreach( $row as $col=>$value )
    {
        switch( $column_types[$col] )
        {
            case 'decimal':
            case 'numeric':
            case 'float':
            case 'double':

                $row[ $col ] = (float)$value;
                break;

            case 'integer':
            case 'int':
            case 'bigint':
            case 'mediumint':
            case 'tinyint':
            case 'smallint':

                $row[ $col ] = (int)$value;
                break;
        }

    }
    $results[ $index ] = $row;
}

switch语句可以轻松修改以适应不同的需求,例如包括日期函数等等。举个例子,在我的情况下,第三方将货币值作为小数推送到数据库中,但是当我获取这些数据并需要将其返回为JSON时,我需要将其转换为数字而不是字符串。

已在PHP 7、7.2和JQuery 2、3中进行测试。


3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接