PHP + PDO + MySQL: how do I return integer and numeric columns from MySQL as integers and numerics in PHP?

85

I've seen this question repeated a few times on Stack Overflow but none sufficiently explore the problem (or at least in a way that is helpful to me)

The problem is that a DB query should return integer data types in PHP for integer columns. Instead the query returns every column as a string type.

I've ensured that "PDO::ATTR_STRINGIFY_FETCHES" if false just to make sure results aren't being cast to string.

Answers that I've seen:

  • It can't be done
    • Nope, it's working on Mac OS X installed PHP/MySQL
  • Type cast all your values in your code
    • Nope, I won't be doing that
  • Don't worry about it, PHP is loosely typed
    • My data is output as JSON and is consumed by many other services, some require the data in the correct format

Из моих исследований я понимаю, что это проблема с реализацией драйвера.

Многие источники утверждают, что собственный драйвер MySQL не поддерживает возвращение числовых типов. Это не кажется правдой, поскольку он работает в Mac OS X. Если только они не хотят сказать, что «собственный драйвер MySQL в Linux не поддерживает эту функцию».

Это означает, что есть что-то особенное в драйвере / среде, которые я установил в Mac OS X. Я пытался определить различия, чтобы применить исправление, но я ограничен своими знаниями о том, как это проверить.

Различия:

  • PHP для OS X был скомпилирован и установлен через Home Brew.
  • PHP на Ubuntu был установлен с помощью "apt-get install php5-dev"
  • PHP в OS X подключается к серверу MySQL, также работающему в OS X
    • Версия сервера: 5.1.71-log Исходный код
  • PHP в Ubuntu подключается к базе данных Rackspace Cloud
    • Версия сервера: 5.1.66-0 + squeeze1 (Debian)

Среда Ubuntu

  • Версия: 10.04.1
  • PHP 5.4.21-1 + debphp.org ~ lucid + 1 (cli) (построено: 21 октября 2013 г., 08:14:37)
  • php -i

    pdo_mysql

    Драйвер PDO для MySQL => включена версия клиентского API => 5.1.72

Среда Mac OS X

  • 10.7.5
  • PHP 5.4.16 (cli) (построено: 22 августа 2013 г., 09:05:58)
  • php -i

    pdo_mysql

    Драйвер PDO для MySQL => включена версия клиентского API => mysqlnd 5.0.10 - 20111026 - $ Id: e707c415db32080b3752b232487a435ee0372157 $

Используемые флаги 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. Я верю
Ваш здравый смысл
У меня точно такой же вопрос, но для MS SQL Server. Есть ли для этого строго типизированная библиотека, похожая на mysqlnd? В последних версиях драйверов я мог бы найти вернуть все строки.
GSerg 09

Ответы:

122

Решение состоит в том, чтобы убедиться, что вы используете драйвер mysqlnd для php.

Как узнать, что вы не используете 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упомянем "mysqlnd" явно в pdo_mysqlразделе:

pdo_mysql

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

Настройки PDO

Убедитесь, что PDO::ATTR_EMULATE_PREPARESэто false(проверьте настройки по умолчанию или установите их):
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

Убедитесь, что PDO::ATTR_STRINGIFY_FETCHESэто false(проверьте настройки по умолчанию или установите их):
$pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);

Возвращенные значения

  • Типы с плавающей точкой (FLOAT, DOUBLE) возвращаются как плавающие PHP.
  • Целочисленные типы (INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINT †) возвращаются как целые числа PHP.
  • Типы с фиксированной точкой (DECIMAL, NUMERIC) возвращаются в виде строк.

† BIGINT со значением больше, чем 64-битное целое число со знаком (9223372036854775807), будет возвращено как строка (или 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
4
Также обратите внимание, что PDO :: ATTR_STRINGIFY_FETCHES не имеет к этому никакого отношения - я пробовал, и это не повлияло на результат, и в этом посте пользователь @Josh Davis говорит, что это не связано с MySQL.
Pere
1
Спасибо, я продолжил расследование и добавил примечания о типе DECIMAL.
Stephenfrank
4
@pere, это правильное поведение. DECIMAL не является числовым типом. Это строковый формат для чисел. Таким образом, вы можете вернуть: 0.30as "0.30"вместо 0.3. Для этого используются десятичные числа ... так что это правильное поведение
Макс Каттинс
почему это чертовски компромисс? Мне нужно PDO::ATTR_EMULATE_PREPARESиспользовать именованный параметр более одного раза
Gaby_64
5

Этот принятый ответ работает и, по-видимому, является самым популярным ответом на этот вопрос в Google. Моя проблема в том, что мне нужно развернуть приложение в нескольких средах, и у меня не всегда есть возможность установить правильный драйвер, плюс мне нужно, чтобы десятичные дроби были числовыми, а не строками. Поэтому я создал процедуру для ввода регистра перед кодировкой JSON, которую легко изменить. Это все равно что сбить его с орбиты.

Во-первых, используйте «показать столбцы из», чтобы получить столбцы из таблицы. mysql query "ПОКАЗАТЬ СТОЛБЦЫ ИЗ таблицы, такой как 'colmunname'": вопросы

$query = 'SHOW COLUMNS FROM ' . $table; //run with mysqli or PDO 

Затем перенесите типы в массив, индексированный по имени столбца, чтобы упростить итерацию. Предполагает, что набор результатов из столбцов отображения находится в переменной с именем $ columns_from_table. http://php.net/manual/en/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.

Крейг Джейкобс
источник