web-dev-qa-db-fra.com

PHP + PDO + MySQL: comment renvoyer des colonnes entières et numériques de MySQL sous forme d'entiers et de chiffres en PHP?

J'ai vu cette question répétée plusieurs fois sur Stack Overflow mais aucun n'explore suffisamment le problème (ou du moins d'une manière qui m'est utile)

Le problème est qu'une requête de base de données doit renvoyer des types de données entiers dans PHP pour les colonnes entières. Au lieu de cela, la requête renvoie chaque colonne sous forme de type chaîne.

J'ai veillé à ce que "PDO :: ATTR_STRINGIFY_FETCHES" si false juste pour s'assurer que les résultats ne sont pas convertis en chaîne.

Réponses que j'ai vues:

  • Cela ne peut pas être fait
    • Non, ça fonctionne sur Mac OS X installé PHP/MySQL
  • Tapez cast toutes vos valeurs dans votre code
    • Non, je ne ferai pas ça
  • Ne vous en faites pas, PHP est tapé de façon lâche
    • Mes données sont sorties en JSON et sont consommées par de nombreux autres services, certains nécessitent les données au format correct

D'après mes recherches, je comprends qu'il s'agit d'un problème de mise en œuvre du pilote.

De nombreuses sources affirment que le pilote natif MySQL ne prend pas en charge le renvoi de types numériques. Cela ne semble pas vrai car cela fonctionne sur Mac OS X. Sauf s'ils veulent dire que "le pilote natif MySQL sur Linux ne prend pas en charge la fonctionnalité".

Cela implique qu'il y a quelque chose de spécial dans le pilote/l'environnement que j'ai installé sur Mac OS X. J'ai essayé d'identifier les différences afin d'appliquer un correctif mais je suis limité par ma connaissance de la façon de vérifier ces choses.

Les différences:

  • PHP sur OS X a été compilé et installé via Home Brew
  • PHP sur Ubuntu a été installé via "apt-get install php5-dev"
  • PHP sur OS X se connecte à un serveur MySQL fonctionnant également sur OS X
    • Version du serveur: distribution source 5.1.71-log
  • PHP sur Ubuntu se connecte à une base de données Cloud Rackspace
    • Version du serveur: 5.1.66-0 + squeeze1 (Debian)

Environnement Ubuntu

  • Version: 10.04.1
  • PHP 5.4.21-1 + debphp.org ~ lucid + 1 (cli) (build: 21 octobre 2013 08:14:37)
  • php -i

    pdo_mysql

    Pilote PDO pour MySQL => version d'API client activée => 5.1.72

Environnement Mac OS X

  • 10.7.5
  • PHP 5.4.16 (cli) (construit: 22 août 2013 09:05:58)
  • php -i

    pdo_mysql

    Pilote PDO pour MySQL => version d'API client activée => mysqlnd 5.0.10 - 20111026 - $ Id: e707c415db32080b3752b232487a435ee0372157 $

Drapeaux AOP utilisés

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,

Toute aide et expertise serait appréciée :) Je reviendrai certainement ici si je trouve la réponse.

74
stephenfrank

La solution est de s'assurer que vous utilisez le pilote mysqlnd pour php.

Comment savez-vous que vous n'utilisez pas mysqlnd?

Lors de l'affichage de php -i, Il n'y aura aucune mention de "mysqlnd". La section pdo_mysql Aura quelque chose comme ceci:

pdo_mysql

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

Comment l'installez-vous?

La plupart des guides d'installation pour L/A/M/P suggèrent apt-get install php5-mysql Mais le pilote natif pour MySQL est installé par un package différent: php5-mysqlnd. J'ai trouvé que c'était disponible avec le ppa: ondrej/php5-oldstable.

Pour basculer vers le nouveau pilote (sur Ubuntu):

  • Supprimez l'ancien pilote:
    apt-get remove php5-mysql
  • Installez le nouveau pilote:
    apt-get install php5-mysqlnd
  • Redémarrez Apache2:
    service Apache2 restart

Comment vérifier que le pilote est utilisé?

Maintenant, php -i Mentionnera explicitement "mysqlnd" dans la section pdo_mysql:

pdo_mysql

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

Paramètres PDO

Assurez-vous que PDO::ATTR_EMULATE_PREPARES Est false (vérifiez vos valeurs par défaut ou définissez-le):
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

Assurez-vous que PDO::ATTR_STRINGIFY_FETCHES Est false (vérifiez vos valeurs par défaut ou définissez-le):
$pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);

Valeurs renvoyées

  • Les types à virgule flottante (FLOAT, DOUBLE) sont retournés sous la forme PHP floats.
  • Les types entiers (INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINT †) sont renvoyés sous la forme PHP nombres entiers).
  • Les types à virgule fixe (DECIMAL, NUMERIC) sont renvoyés sous forme de chaînes.

† Les BIGINT avec une valeur supérieure à un entier signé 64 bits (9223372036854775807) seront renvoyés sous forme de chaîne (ou 32 bits sur un système 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)
112
stephenfrank

Cette réponse acceptée fonctionne et semble être la réponse la plus populaire sur Google pour cette question. Mon problème est que je dois déployer une application dans plusieurs environnements et que je n'ai pas toujours la possibilité d'installer le bon pilote, et que j'ai besoin que les décimales soient numériques, pas des chaînes. J'ai donc créé une routine pour taper la casse avant l'encodage JSON, qui est facilement modifiée pour convenir. C'est un peu comme le mettre en orbite.

Tout d'abord, utilisez "afficher les colonnes de" pour obtenir les colonnes du tableau. requête mysql "SHOW COLUMNS FROM table like 'colmunname'": questions

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

Ensuite, placez les types dans un tableau indexé par le nom de la colonne pour faciliter l'itération. Suppose que l'ensemble de résultats des colonnes show se trouve dans une variable nommée $ column_from_table. http://php.net/manual/en/function.array-column.php

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

Ensuite, nous voulons supprimer la parenthèse du type, qui va être quelque chose comme varchar (32) ou décimal (14,6)

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

Nous avons maintenant un tableau associé avec le nom de la colonne comme index et le type formaté comme valeur, par exemple:

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

Maintenant, lorsque vous effectuez votre sélection dans votre tableau, vous pouvez parcourir les résultats et convertir la valeur en type approprié:

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

L'instruction switch peut être facilement modifiée pour convenir, et peut inclure des fonctions de date, etc. Par exemple, dans mon cas, un tiers pousse les valeurs monétaires dans la base de données sous forme décimale, mais lorsque j'attrape ces données et que je dois les renvoyer sous la forme JSON, j'ai besoin que ce soient des nombres, pas des chaînes.

Testé avec PHP 7, 7.2 et JQuery 2, 3.

2
Craig Jacobs