8

我在我的 PHP 脚本中遇到问题,其中从 MySQL 调用的值作为字符串返回,尽管在数据库中被标记为inttinyint.

这是一个问题,因为在将基于 MySQL 日期的数组转换为 JSON 数据时,应该是整数的值放在双引号中,这会在使用该 JSON 数据的 Javascript 和 iPhone 应用程序中造成问题。我得到的 JSON 值看起来像"key" : "1",当我想要的是"key" : 1

在做了一些研究之后,似乎只要安装了 PHP 5.3 并安装了模块,就应该可以将这些值作为它们的本机类型。mysqlnd我有 5.3.3,phpinfo()似乎表明我已经mysqlnd安装并运行了模块:

mysqlnd enabled
Version mysqlnd 5.0.10 - 20111026

但是,我的值仍然作为字符串返回。

我查看了mysqlnd 的 PHP 手册条目,我总是可能遗漏了显而易见的内容,但我没有看到任何表明我需要在代码中执行任何特定操作来获取本机值的内容。

我到底该怎么做才能让我的 MySQL 函数在 PHP 中以它们的本机类型为我提供 MySQL 结果?


为了使下面的答案更加吸引人,这是我用来连接数据库的命令:

private function databaseConnect()
{
    $this->mysqli = new mysqli(Database::$DB_SERVER, Database::$DB_USERNAME, Database::$DB_PASSWORD);
    $this->mysqli->set_charset("utf8");
    return true;
}

private function dbConnect()
{
    Database::$USE_MYSQLI = extension_loaded('mysqli');
    if (!$this->databaseConnect())
    {
        echo "Cannot Connect To The Database Server";
        throw new Exception();
    }
    if (!$this->databaseSelectDB())
    {
        echo "The database server connected, but the system could not find the right database";
        throw new Exception();
    }
}

private function databaseQuery($query)
{
    return $this->mysqli->query($query);
}

public function doQuery($query)
{
    $result = $this->databaseQuery($query);
    if ($result == FALSE)
    {
        //ErrorHandler::backtrace();
        die("This query did not work: $query");
    }
    return $result;
}

private function getRows($table, $matches, $orderBy = array(), $limit = array())
{
    $calcFoundRows = '';
    if (count($limit) > 0)
    {
        $calcFoundRows = ' SQL_CALC_FOUND_ROWS';
    }
    $query = 'SELECT ' . $calcFoundRows . ' * FROM ' . $table;
    if (count($matches) > 0)
    {
        $query .= ' WHERE ';
        $keys = array_keys($matches);
        $first = true;
        foreach ($keys as $key)
        {
            if (!$first)
            {
                $query .= ' AND ';
            }
            $first = false;

            // now he is safe to add to the query
            // the only time this is an array is when this is called by getSelectedUsers or getSelectedArticles
            // in that case it is an array of array's as the key (which is the column name) may have more than
            // one condition
            if (is_array($matches[$key]))
            {
                $firstForColumn = true;
                foreach ($matches[$key] as $conditions)
                {
                    if (!$firstForColumn)
                    {
                        $query .= ' AND ';
                    }
                    $firstForColumn = false;

                    // if the value is an array we generate an OR selection
                    if (is_array($conditions[1]))
                    {
                        $firstOr = true;
                        $query .= '(';

                        foreach ($conditions[1] as $value)
                        {
                            if (!$firstOr)
                            {
                                $query .= ' OR ';
                            }
                            $firstOr = false;
                            // clean this guy before putting him into the query
                            $this->cleanMySQLData($value);
                            if ($conditions[0] == Selection::$CONTAINS)
                            {
                                //$query .= 'MATCH (' . $key . ') AGAINST (' . $value . ') ';
                                $value = trim($value, "'");
                                $value = "'%" . $value . "%'";
                                $query .= $key . ' LIKE ' . $value;
                            }
                            else
                            {
                                $query .= $key . ' ' . $conditions[0] . ' ' . $value;
                            }
                        }

                        $query .= ')';
                    }
                    else
                    {
                        // clean this guy before putting him into the query
                        $var = $conditions[1];
                        $this->cleanMySQLData($var);
                        if ($conditions[0] == Selection::$CONTAINS)
                        {
                            //$query .= 'MATCH (' . $key . ') AGAINST (' . $var . ') ';
                            $var = trim($var, "'");
                            $var = "'%" . $var . "%'";
                            $query .= $key . ' LIKE ' . $var;
                        }
                        else
                        {
                            $query .= $key . ' ' . $conditions[0] . ' ' . $var;
                        }
                    }
                }
            }
            else
            {
                // clean this guy before putting him into the query
                $this->cleanMySQLData($matches[$key]);
                $query .= $key . " = " . $matches[$key];
            }
        }
    }
    if (count($orderBy) > 0)
    {
        $query .= " ORDER BY ";
        $first = true;
        foreach ($orderBy as $orderCol)
        {
            if (!$first)
            {
                $query .= ',';
            }
            $query .= $orderCol;
            $first = false;
        }
    }

    if (count($limit) > 0)
    {
        $query .= ' LIMIT ' . $limit[0];
        if (count($limit) > 1)
        {
            $query .= ',' . $limit[1];
        }
    }


    $result = $this->doQuery($query);
    $data = array();
    while ($row = $this->databaseFetchAssoc($result))
    {
        $data[] = $row;
    }
    if (strlen($calcFoundRows) > 0)
    {
        $numRows = $this->databaseCountFoundRows();
        $key = '^^' . $table . '_selectionCount';
        Session::getSession()->putUserSubstitution($key, $numRows);
    }

    return $data;
}
4

5 回答 5

4

我到底该怎么做才能让我的 MySQL 函数在 PHP 中以它们的本机类型为我提供 MySQL 结果?

您连接到数据库,然后准备查询,执行它,绑定结果,然后获取它。

让我们逐行执行这些步骤:

$conn = new Mysqli('localhost', 'testuser', 'test', 'test');
$stmt = $conn->prepare("SELECT id FROM config LIMIT 1");
$stmt->execute();
$stmt->bind_result($id);
$stmt->fetch();
var_dump($id); # it's an int!

这对我有用。由于您编写的代码更加复杂,因此您需要找到查询数据库的位置。检查您是否正在使用Mysqli::prepare(),如果没有,请介绍它。

您还需要使用Mysqli_Stmt::execute()Mysqli_Stmt::bind_result()否则不会为该结果列保留(此处为整数)类型。

于 2013-01-06T10:43:47.113 回答
3

但是,我的值仍然作为字符串返回。

你在 PHP 中,你的数据是 int、bool、strings 都没有关系......如果它们有这样的类型,它们被称为标量数据,动态转换将允许你让它们按照你想要的方式运行。例如,字符串"12345"+"54321"会给你66666. 如果您绝对希望您的数据属于特定类型,就像在每种语言中一样,这不是驱动程序的工作。在 Java 中,您有类似JDBC 接口中.getString.getInt方法,在 PHP 中您没有,因为它不是很有用。intval boolval strval您将不得不使用... 函数或(int), (bool)... 转换运算符来转换您的数据。

正如您的帖子所说,您可以使用服务器端准备好的语句来获得它:

使用 mysqlnd 进行 PDO 的优势

mysqlnd 在使用服务器端准备语句时返回本机数据类型,例如,INT 列作为整数变量而不是字符串返回。这意味着内部的数据转换更少。

带 PDO

您必须在连接后放置此行

$PDO->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);

然后,当您要查询时:

$s = $PDO->prepare('yourquery');
//here, binding params
$s->bindValue('paramName','paramValue');
$s->execute();

使用 Mysqli

当您使用 mysqli 时,语法会有所不同: 注意:没有客户端准备好的语句,因此您不需要我在 PDO 中放置的配置行。

因此,您的查询将如下所示:

$statement = $MySQli->prepare('your query');
$statement->bind_param('si', $stringParam, $intParam);
$statement->bind_result($var1, $var2 /*,...*/);
$statement->execute();
while($statement->fetch()){
  //process here, result will be in var1, var2...
}

可以看到,这里没有内置的 fetchAll 方法。要绑定您的数据,您需要使用变量,因为它不像 in 那样作为值传递,PDOStatement::bindValue()而是通过引用传递。此外,类型在第一个 arg 中定义(s 代表字符串,i 代表整数......)没有命名参数,只有索引参数。该fetch方法以不同的方式工作,需要您在执行语句之前调用 bind_result;

于 2013-01-06T09:55:21.967 回答
2

默认情况下为准备好的语句启用它。如果您一直使用准备好的语句,那么您已经以正确的类型返回了数据。

$stmt = $mysqli->prepare('SELECT 1');
$stmt->execute();
$ret = $stmt->get_result();
echo gettype($ret->fetch_row()[0]);

上面的代码将返回:

整数

但是,如果您使用正常的未准备查询,那么即使使用 mysqlnd,所有值也会作为字符串返回。但是您也可以启用相同的设置以应用于未准备的查询。

$ret = $mysqli->query('SELECT 1');
echo gettype($ret->fetch_row()[0]);
// outputs:
// string

// Enable the same setting for unprepared queries
mysqli_options($mysqli, MYSQLI_OPT_INT_AND_FLOAT_NATIVE, true);

$ret = $mysqli->query('SELECT 1');
echo gettype($ret->fetch_row()[0]);
// outputs:
// integer

原因是mysqlnd的技术实现。这同样适用于 PDO_MYSQL,但在 PDO 中,当您关闭模拟准备好的语句时,默认情况下会启用此设置。Ulf Wendel在您链接的其中一篇文章中有一个很好的解释。

这是针对 mysqlnd 编译时 PDO_MYSQL 的新默认值,关闭 PDO 准备语句仿真并禁用PDO::ATTR_STRINGIFY_FETCHES. MySQL Prepared Statements 使用不同的二进制通信协议但非准备语句。二进制协议避免了不必要的字符串类型转换。

Julien Pauli 在此演示文稿中解释了文本和二进制 MySQL 协议之间的区别:https ://www.slideshare.net/jpauli/mysqlnd

于 2020-05-13T00:19:36.020 回答
1

使用 mysqlnd 可以做到这一点,但只有在您启用它之后。要启用它,您需要调用该方法options并将选项设置为 true:

$this->mysqli->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, true); 
于 2020-03-30T15:24:22.223 回答
0

这可能不是最好的答案,但您可以使用诸如intval()之类的东西来强化数据类型。在使用 PDO、Mysqli 甚至旧的(现已弃用的)mysql 函数时,我个人有过这种经验。您可以运行测试用例is_int()is_string()100% 确定从数据库中获取的数据确实是字符串与 int。对不起,我无法提供更多帮助!

于 2013-01-06T09:54:44.233 回答