PHP + PDO + MySQL: ¿cómo devuelvo columnas enteras y numéricas de MySQL como enteros y numéricos en PHP?

85

He visto repetirse esta pregunta varias veces en Stack Overflow, pero ninguna explora suficientemente el problema (o al menos de una manera que me sea útil)

El problema es que una consulta de base de datos debería devolver tipos de datos enteros en PHP para columnas enteras. En cambio, la consulta devuelve cada columna como un tipo de cadena.

Me he asegurado de que "PDO :: ATTR_STRINGIFY_FETCHES" sea falso solo para asegurarme de que los resultados no se conviertan en cadenas.

Respuestas que he visto:

  • No se puede hacer
    • No, está funcionando en Mac OS X instalado PHP / MySQL
  • Escriba emitir todos sus valores en su código
    • No, no haré eso
  • No se preocupe por eso, PHP está escrito de manera flexible
    • Mis datos se envían como JSON y son consumidos por muchos otros servicios, algunos requieren los datos en el formato correcto

De mi investigación, entiendo que este es un problema de implementación de controladores.

Muchas fuentes afirman que el controlador nativo de MySQL no admite la devolución de tipos numéricos. Esto no parece cierto ya que funciona en Mac OS X. A menos que quieran decir que "el controlador nativo de MySQL en Linux no es compatible con la función".

Esto implica que hay algo especial en el controlador / entorno que he instalado en Mac OS X. He estado tratando de identificar las diferencias para aplicar una solución, pero estoy limitado por mi conocimiento de cómo verificar estas cosas.

Las diferencias:

  • PHP en OS X se compiló e instaló a través de Home Brew
  • PHP en Ubuntu se instaló a través de "apt-get install php5-dev"
  • PHP en OS X se está conectando a un servidor MySQL que también se ejecuta en OS X
    • Versión del servidor: distribución de origen 5.1.71-log
  • PHP en Ubuntu se está conectando a una base de datos en la nube de Rackspace
    • Versión del servidor: 5.1.66-0 + squeeze1 (Debian)

Entorno Ubuntu

  • Versión: 10.04.1
  • PHP 5.4.21-1 + debphp.org ~ lucid + 1 (cli) (construido: 21 de octubre de 2013 08:14:37)
  • php -i

    pdo_mysql

    Controlador PDO para MySQL => versión API de cliente habilitada => 5.1.72

Entorno Mac OS X

  • 10.7.5
  • PHP 5.4.16 (cli) (construido: 22 de agosto de 2013 09:05:58)
  • php -i

    pdo_mysql

    Controlador PDO para MySQL => versión de API de cliente habilitada => mysqlnd 5.0.10 - 20111026 - $ Id: e707c415db32080b3752b232487a435ee0372157 $

Indicadores de DOP utilizados

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,

Se agradecería cualquier ayuda y experiencia :) Definitivamente volveré a publicar aquí si encuentro la respuesta.

Stephenfrank
fuente
2
Es responsabilidad de mysqlnd. Creo
tu sentido común
Tengo exactamente la misma pregunta, pero para MS SQL Server. ¿Existe una biblioteca fuertemente tipada para eso, similar a mysqlnd? Los últimos controladores que pude encontrar devuelven todas las cadenas.
GSerg

Respuestas:

121

La solución es asegurarse de que está utilizando el controlador mysqlnd para php.

¿Cómo sabe que no está usando mysqlnd?

Cuando se ven php -i, no habrá ninguna mención de "mysqlnd". La pdo_mysqlsección tendrá algo como esto:

pdo_mysql

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

¿Cómo lo instalas?

La mayoría de las guías de instalación de L / A / M / P sugieren apt-get install php5-mysqlpero el controlador nativo de MySQL se instala mediante un paquete diferente: php5-mysqlnd. Descubrí que esto estaba disponible con ppa: ondrej / php5-oldstable .

Para cambiar al nuevo controlador (en Ubuntu):

  • Quite el controlador anterior:
    apt-get remove php5-mysql
  • Instale el nuevo controlador:
    apt-get install php5-mysqlnd
  • Reinicie apache2:
    service apache2 restart

¿Cómo verifico que se esté utilizando el controlador?

Ahora php -imencionaré "mysqlnd" explícitamente en la pdo_mysqlsección:

pdo_mysql

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

Configuración de PDO

Asegúrese de que PDO::ATTR_EMULATE_PREPARESsea false(verifique sus valores predeterminados o configúrelo):
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

Asegúrese de que PDO::ATTR_STRINGIFY_FETCHESsea false(verifique sus valores predeterminados o configúrelo):
$pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);

Valores devueltos

  • Los tipos de coma flotante (FLOAT, DOUBLE) se devuelven como flotantes PHP.
  • Los tipos de enteros (INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINT †) se devuelven como enteros de PHP.
  • Los tipos de punto fijo (DECIMAL, NUMERIC) se devuelven como cadenas.

† Los BIGINT con un valor mayor que un int con signo de 64 bits (9223372036854775807) regresarán como una cadena (o 32 bits en un sistema de 32 bits)

    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)
Stephenfrank
fuente
3
Tenga en cuenta que incluso con esta versión de mysqlnd habilitada, DECIMALS todavía se enviarán como cadenas ... (¡aunque FLOATS se devolverán como flotantes!): / Fuente: Lo probé.
Pere
4
También tenga en cuenta que PDO :: ATTR_STRINGIFY_FETCHES no tiene nada que ver con eso; lo intenté y no tiene ningún efecto en el resultado, y en esta publicación, el usuario @Josh Davis dice que no está relacionado con MySQL.
Pere
1
Gracias, he investigado más y actualizado con notas sobre el tipo DECIMAL.
Stephenfrank
4
@pere este es el comportamiento correcto. DECIMAL no es un tipo numérico. Es un formato de cadena para números. Entonces puedes regresar: 0.30como en "0.30"lugar de 0.3. Los decimales se utilizan para esto ... así que este es el comportamiento correcto
Max Cuttins
¿Por qué es un maldito compromiso? Necesito PDO::ATTR_EMULATE_PREPARESusar un parámetro con nombre más de una vez
Gaby_64
4

Esta respuesta aceptada funciona y parece ser la respuesta más popular en Google para esta pregunta. Mi problema es que necesito implementar una aplicación en múltiples entornos y no siempre tengo la capacidad de instalar el controlador correcto, además necesito que los decimales sean numéricos, no cadenas. Así que creé una rutina para escribir mayúsculas y minúsculas antes de la codificación JSON, que se modifica fácilmente para adaptarse. Es como destruirlo desde la órbita.

Primero, use "mostrar columnas de" para obtener las columnas de la tabla. consulta de mysql "MOSTRAR COLUMNAS DE la tabla como 'colmunname'": preguntas

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

Luego, coloque los tipos en una matriz indexada por el nombre de la columna para facilitar la iteración. Supone que el conjunto de resultados de mostrar columnas está en una variable denominada $ columnas_de_tabla. http://php.net/manual/en/function.array-column.php

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

A continuación, queremos eliminar el paréntesis del tipo, que será algo así como varchar (32) o decimal (14,6).

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

Ahora tenemos una matriz asociada con el nombre de la columna como índice y el tipo formateado como valor, por ejemplo:

Array
(
    [id] => int
    [name] => varchar
    [balance] => decimal
    ...
)

Ahora, cuando haga su selección de su tabla, puede iterar sobre los resultados y convertir el valor al tipo apropiado:

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;
}

La declaración de cambio se puede modificar fácilmente para adaptarse y podría incluir funciones de fecha, etc. Por ejemplo, en mi caso, un tercero está introduciendo valores de moneda en la base de datos como un decimal, pero cuando obtengo esos datos y necesito devolverlos como JSON, necesito que sean números, no cadenas.

Probado con PHP 7, 7.2 y JQuery 2, 3.

Craig Jacobs
fuente