PHP + PDO + MySQL: como faço para retornar colunas inteiras e numéricas do MySQL como inteiros e numéricos em PHP?

85

Já vi essa pergunta ser repetida algumas vezes no Stack Overflow, mas nenhuma explora suficientemente o problema (ou pelo menos de uma forma que seja útil para mim)

O problema é que uma consulta de banco de dados deve retornar tipos de dados inteiros em PHP para colunas inteiras. Em vez disso, a consulta retorna cada coluna como um tipo de string.

Eu assegurei que "PDO :: ATTR_STRINGIFY_FETCHES" seja falso apenas para ter certeza de que os resultados não estão sendo convertidos em string.

Respostas que tenho visto:

  • Não pode ser feito
    • Não, está funcionando em Mac OS X instalado em PHP / MySQL
  • Digite elenco de todos os seus valores em seu código
    • Não, eu não vou fazer isso
  • Não se preocupe com isso, o PHP é mal digitado
    • Meus dados são enviados como JSON e são consumidos por muitos outros serviços, alguns requerem os dados no formato correto

Pela minha pesquisa, entendo que este é um problema de implementação do driver.

Muitas fontes afirmam que o driver nativo do MySQL não suporta o retorno de tipos numéricos. Isso não parece verdade, pois funciona no Mac OS X. A menos que eles digam que "o driver nativo do MySQL no Linux não suporta o recurso".

Isso implica que há algo especial sobre o driver / ambiente que instalei no Mac OS X. Tenho tentado identificar as diferenças para aplicar uma correção, mas estou limitado por meu conhecimento de como verificar essas coisas.

As diferenças:

  • PHP no OS X foi compilado e instalado através do Home Brew
  • PHP no Ubuntu foi instalado via "apt-get install php5-dev"
  • PHP no OS X está se conectando a um servidor MySQL também rodando no OS X
    • Versão do servidor: 5.1.71-log de distribuição de origem
  • PHP no Ubuntu está se conectando a um banco de dados Rackspace Cloud
    • Versão do servidor: 5.1.66-0 + squeeze1 (Debian)

Ambiente Ubuntu

  • Versão: 10.04.1
  • PHP 5.4.21-1 + debphp.org ~ lucid + 1 (cli) (construído em: 21 de outubro de 2013 08:14:37)
  • php -i

    pdo_mysql

    Driver PDO para MySQL => versão API do cliente habilitada => 5.1.72

Ambiente Mac OS X

  • 10.7.5
  • PHP 5.4.16 (cli) (construído em: 22 de agosto de 2013 09:05:58)
  • php -i

    pdo_mysql

    Driver PDO para MySQL => versão API do cliente habilitada => mysqlnd 5.0.10 - 20111026 - $ Id: e707c415db32080b3752b232487a435ee0372157 $

Sinalizadores PDO usados

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,

Qualquer ajuda e conhecimento seriam apreciados :) Definitivamente estarei postando de volta aqui se eu encontrar a resposta.

Stephenfrank
fonte
2
É responsabilidade do mysqlnd. Eu acredito
Seu Bom Senso
Tenho exatamente a mesma pergunta, mas para o MS SQL Server. Existe uma biblioteca fortemente tipada para isso, semelhante a mysqlnd? Os drivers mais recentes que consegui encontrar retornam todas as strings.
GSerg

Respostas:

122

A solução é garantir que você está usando o driver mysqlnd para php.

Como você sabe que não está usando o mysqlnd?

Ao visualizar php -i, não haverá nenhuma menção de "mysqlnd". A pdo_mysqlseção terá algo assim:

pdo_mysql

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

Como você instala?

A maioria dos guias de instalação para L / A / M / P sugerem apt-get install php5-mysqlmas o controlador nativo para MySQL é instalado por um pacote diferente: php5-mysqlnd. Descobri que isso estava disponível com o ppa: ondrej / php5-oldstable .

Para mudar para o novo driver (no Ubuntu):

  • Remova o driver antigo:
    apt-get remove php5-mysql
  • Instale o novo driver:
    apt-get install php5-mysqlnd
  • Reinicie o apache2:
    service apache2 restart

Como verifico se o driver está sendo usado?

Agora php -imencionarei "mysqlnd" explicitamente na pdo_mysqlseção:

pdo_mysql

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

Configurações de PDO

Certifique-se de que PDO::ATTR_EMULATE_PREPARESé false(verifique seus padrões ou defina-o):
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

Certifique-se de que PDO::ATTR_STRINGIFY_FETCHESé false(verifique seus padrões ou defina-o):
$pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);

Valores retornados

  • Tipos de ponto flutuante (FLOAT, DOUBLE) são retornados como flutuadores PHP.
  • Tipos inteiros (INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINT †) são retornados como inteiros PHP.
  • Tipos de ponto fixo (DECIMAL, NUMERIC) são retornados como strings.

† BIGINTs com um valor maior que um int assinado de 64 bits (9223372036854775807) retornarão como uma string (ou 32 bits em um 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
fonte
3
Note que mesmo com esta versão do mysqlnd habilitada, DECIMALS ainda será enviado como strings ... (embora FLOATS seja retornado como floats!): / Fonte: Eu testei.
Pere
4
Observe também que PDO :: ATTR_STRINGIFY_FETCHES não tem nada a ver com isso - eu tentei e não teve efeito no resultado, e neste post o usuário @Josh Davis diz que não está relacionado ao MySQL.
Pere
1
Obrigado, eu investiguei mais e atualizei com notas sobre o tipo DECIMAL.
stephenfrank
4
@pere, este é o comportamento correto. DECIMAL não é um tipo numérico. É um formato de string para números. Portanto, você pode retornar: as 0.30em "0.30"vez de 0.3. Decimais são usados ​​para isso ... então este é o comportamento correto
Max Cuttins
por que é um compromisso maldito? Preciso PDO::ATTR_EMULATE_PREPARESusar um parâmetro nomeado mais de uma vez
Gaby_64
5

Essa resposta aceita funciona e parece ser a resposta mais popular no Google para essa pergunta. Meu problema é que preciso implantar um aplicativo em vários ambientes e nem sempre consigo instalar o driver correto, além disso, preciso que os decimais sejam numéricos, não sequências. Portanto, criei uma rotina para digitar maiúsculas e minúsculas antes da codificação JSON, que é facilmente modificada para se adequar. É meio como explodir em órbita.

Primeiro, use "mostrar colunas de" para obter as colunas da tabela. consulta mysql "MOSTRAR COLUNAS DA tabela como 'nomedocolmun'": questões

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

Em seguida, coloque os tipos em uma matriz indexada pelo nome da coluna para facilitar a iteração. Assume que o conjunto de resultados de colunas de exibição está em uma variável chamada $ colunas_de_tabela. http://php.net/manual/en/function.array-column.php

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

Em seguida, queremos remover o parênteses do tipo, que será algo como varchar (32) ou decimal (14,6)

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

Agora temos uma matriz associada com o nome da coluna como o índice e o tipo formatado como o valor, por exemplo:

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

Agora, quando você faz sua seleção na tabela, pode iterar os resultados e converter o valor para o tipo apropriado:

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

A instrução switch pode ser facilmente modificada para se adequar e pode incluir funções de data, etc. Por exemplo, no meu caso, um terceiro está inserindo valores de moeda no banco de dados como um decimal, mas quando eu pego esses dados e preciso retorná-los como JSON, preciso que sejam números, não strings.

Testado com PHP 7, 7.2 e JQuery 2, 3.

Craig Jacobs
fonte