Le champ entier MySQL est renvoyé sous forme de chaîne en PHP


127

J'ai un champ de table dans une base de données MySQL:

userid INT(11)

Je l'appelle donc sur ma page avec cette requête:

"SELECT userid FROM DB WHERE name='john'"

Ensuite, pour gérer le résultat, je fais:

$row=$result->fetch_assoc();

$id=$row['userid'];

Maintenant si je fais:

echo gettype($id);

Je reçois une chaîne. Cela ne devrait-il pas être un entier?


2
la requête MySQL donne des valeurs de ligne et non des types de ligne ou toute autre information associée. Juste les données brutes qui se trouvent dans chaque cellule du tableau. Vous devrez en tenir compte en PHP
Dan Hanly

Si vous avez besoin des types de colonnes, voir ici forums.whirlpool.net.au/archive/526795
RichardTheKiwi

Pour les lecteurs, la réponse sélectionnée consiste à parcourir les résultats. Mais il existe une meilleure solution. stackoverflow.com/a/1197424/5079380
Amr ElAdawy

1
@DanHanly - techniquement, la requête MYSQL (en PHP) renvoie une représentation sous forme de chaîne de la valeur de la cellule - la valeur réelle de la cellule stockée dans la base de données pour un entier 32 bits est ... un entier 32 bits, pas une chaîne de chiffres .
ToolmakerSteve

pour moi, lorsque j'ai utilisé, $conn->query("your query")j'ai obtenu les champs entiers sous forme de chaîne, mais lorsque j'ai utilisé, $conn->prepare("your query")j'ai obtenu les paramètres tels qu'ils étaient dans la base de données
Bar Tzadok

Réponses:


129

Lorsque vous sélectionnez des données dans une base de données MySQL en utilisant PHP, le type de données sera toujours converti en chaîne. Vous pouvez le reconvertir en entier à l'aide du code suivant:

$id = (int) $row['userid'];

Ou en utilisant la fonction intval():

$id = intval($row['userid']);

79
Étant venu à MySQL à partir d'un arrière-plan Postgres, je ressens le besoin de dire que c'est une énorme douleur dans le cul. Lorsque vous récupérez une ligne dans Postgres, vous pouvez vous assurer que les éléments du tableau de lignes ont des types de données appropriés. Maintenant, je dois écrire du code pour convertir manuellement chaque élément dans le type de données que j'attends.
GordonM

25
Je viens de faire quelques tests sur Windows avec Laravel et Mysql sur le même serveur de schéma et de base de données. Sous Windows, la clé primaire est renvoyée sous la forme d'un entier et sous Linux, c'est une chaîne.
AturSams

Ok c'est une bonne idée mais que faire si vous avez des milliers de champs à récupérer? Ce processus prend un certain temps .. Dans mon exemple je récupère le nombre de vues pour chaque article, puis j'imprime tout mon article dans un tableau, j'autorise l'utilisateur à trier par vue asc et desc, mais il trie par "1" à "9 "ou" 9 "à" 1 "donc en premier lieu vous pourriez avoir" 9999 "," 91 "," 8111 "," 7 ", etc ...
KeizerBridge

@zehelvion avez-vous déjà trouvé une solution à cela?
Felipe Francisco

2
Chaque champ retourné sera une chaîne OU NULL .
Liam

73

Utilisez le mysqlnd (pilote natif) pour php.

Si vous êtes sur Ubuntu:

sudo apt-get install php5-mysqlnd
sudo service apache2 restart

Si vous êtes sur Centos:

sudo yum install php-mysqlnd
sudo service httpd restart

Le pilote natif renvoie les types entiers de manière appropriée.

Éditer:

Comme @Jeroen l'a souligné, cette méthode ne fonctionnera que pour PDO.
Comme @LarsMoelleken l'a souligné, cette méthode fonctionnera avec mysqli si vous définissez également l'option MYSQLI_OPT_INT_AND_FLOAT_NATIVE sur true.

Exemple:

$mysqli = mysqli_init();
$mysqli->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, TRUE);

Je ne vois aucune preuve de cette attitude. Fonctionnant avec mysqlnd et mysqli, les valeurs de chaîne sont clairement renvoyées.
Jeroen

@Jeroen êtes-vous sûr? Il y a beaucoup de documentation disponible. stackoverflow.com/questions/1197005/…
avance

Oui. Regardez également les commentaires ci-dessous. Avez-vous testé cela vous-même? dans l'état actuel des choses, même mysqlnd ne retourne que les types appropriés si vous utilisez des instructions préparées. exemple: $link = mysqli_connect('localhost', 'root', ''); mysqli_set_charset($link,'utf8'); $result = mysqli_query($link,'SELECT CAST(\'3.51\' AS DECIMAL(3,2))'); $row = mysqli_fetch_assoc($result); var_dump($row);renvoie: array(1) { ["CAST('3.51' AS DECIMAL(3,2))"]=> string(4) "3.51" }
Jeroen

1
Pas comme je le comprends, cela devrait fonctionner aussi bien avec PDO que Mysqli. Même sur le lien que vous publiez ci-dessus dans le commentaire, il est clairement indiqué que cela fonctionne UNIQUEMENT AVEC LES ÉNONCÉS PRÉPARÉS et non les requêtes en ligne selon le PO. citation de cette page: Mais il ne s'agit que de PHP 5.3 (à condition que votre version de PHP 5.3 soit compilée avec mysqlnd (et non l'ancienne libmysql)), et semble être le cas uniquement pour les instructions préparées :-(
Jeroen

7
vous pouvez également utiliser "MYSQLI_OPT_INT_AND_FLOAT_NATIVE" pour mysqli
Lars Moelleken

20

Solution la plus simple que j'ai trouvée:

Vous pouvez forcer json_encode à utiliser des nombres réels pour les valeurs qui ressemblent à des nombres:

json_encode($data, JSON_NUMERIC_CHECK) 

(depuis PHP 5.3.3).

Ou vous pouvez simplement convertir votre identifiant en un int.

$row = $result->fetch_assoc();
$id = (int) $row['userid'];

1
Utiliser un json_encodepour lancer une chaîne en int, si possible, c'est comme utiliser un microscope pour étrangler les ongles.
LibertyPaul

7
Cela cassera tous les codes postaux et le numéro de téléphone. Bon moyen d'introduire des bogues dans votre application. La solution DOIT respecter le type de données de la colonne mysql, ne pas essayer de deviner à partir de la valeur.
Max Cuttins

1
Max Cuttins, votre point est valable. Cependant, si vous pouvez prendre en compte tous les types de données attendus de la base de données, ce serait alors une voie parfaite à prendre.
Ifedi Okonkwo

1
Cela vous blessera lorsque vous aurez parfois des identifiants tels que «06». Cet indicateur le convertira en 6. Ce qui est faux car ces identifiants devraient garder des zéros à la fin.
Hassan Dad Khan

C'est une excellente solution pour les valeurs créées par UNIX_TIMESTAMP(), par exemple.
David le

18

Ma solution consiste à transmettre le résultat de la requête $rset à obtenir un tableau assoc des données castées comme retour:

function cast_query_results($rs) {
    $fields = mysqli_fetch_fields($rs);
    $data = array();
    $types = array();
    foreach($fields as $field) {
        switch($field->type) {
            case 3:
                $types[$field->name] = 'int';
                break;
            case 4:
                $types[$field->name] = 'float';
                break;
            default:
                $types[$field->name] = 'string';
                break;
        }
    }
    while($row=mysqli_fetch_assoc($rs)) array_push($data,$row);
    for($i=0;$i<count($data);$i++) {
        foreach($types as $name => $type) {
            settype($data[$i][$name], $type);
        }
    }
    return $data;
}

Exemple d'utilisation:

$dbconn = mysqli_connect('localhost','user','passwd','tablename');
$rs = mysqli_query($dbconn, "SELECT * FROM Matches");
$matches = cast_query_results($rs);
// $matches is now a assoc array of rows properly casted to ints/floats/strings

2
Excellente solution sauf que 'NULL' est également un type de variable utilisé par la fonction settype (). Ainsi, votre code change toutes les valeurs NULL retournées par la base de données (valeurs où gettype () == 'NULL') en type 'string' avec '' value '', 'integer' avec 0 valeur ou 'float' avec 0.0. Vous n'avez pas besoin d'appeler settype si is_null ($ data [$ i] [$ name]) est vrai.
Winter Dragoness

J'aime la technique de mastermind, mais le codage peut être plus simple .
ToolmakerSteve

13

Indépendamment du type de données défini dans vos tables, le pilote MySQL de PHP sert toujours les valeurs de ligne comme des chaînes.

Vous devez convertir votre identifiant en int.

$row = $result->fetch_assoc();
$id = (int) $row['userid'];

6
Ce n'est pas PHP qui est responsable (directement) de ce comportement, c'est le pilote de la base de données. Si vous vous interfacez avec une base de données Postgres de PHP, vous récupérerez les types de données appropriés.
GordonM

5

J'aime la réponse de Chad, en particulier lorsque les résultats de la requête seront transmis à javascript dans un navigateur. Javascript traite proprement les entités numériques comme des nombres, mais nécessite un travail supplémentaire pour traiter les entités numériques comme des chaînes. ie doit utiliser parseInt ou parseFloat sur eux.

En m'appuyant sur la solution de Chad, j'utilise ceci et c'est souvent exactement ce dont j'ai besoin et crée des structures qui peuvent être encodées en JSON pour une utilisation facile en javascript.

while ($row = $result->fetch_assoc()) {
    // convert numeric looking things to numbers for javascript
    foreach ($row as &$val) {
        if (is_numeric($val))
            $val = $val + 0;
    }
}

L'ajout d'une chaîne numérique à 0 produit un type numérique en PHP et identifie correctement le type afin que les nombres à virgule flottante ne soient pas tronqués en entiers.


5

Cela se produit lorsque PDO::ATTR_EMULATE_PREPARESest défini sur truesur la connexion.

Attention cependant, le paramétrer sur falseinterdit l'utilisation de paramètres plus d'une fois. Je pense que cela affecte également la qualité des messages d'erreur qui reviennent.


2

Vous pouvez le faire avec ...

  1. mysql_fetch_field ()
  2. mysqli_result :: fetch_field_direct ou
  3. PDOStatement :: getColumnMeta ()

... selon l'extension que vous souhaitez utiliser. Le premier n'est pas recommandé car l'extension mysql est obsolète. Le troisième est encore expérimental.

Les commentaires sur ces hyperliens expliquent bien comment définir votre type d'une ancienne chaîne simple à son type d'origine dans la base de données.

Certains frameworks font également abstraction de cela (CodeIgniter fournit $this->db->field_data()).

Vous pouvez également faire des suppositions - comme faire une boucle sur vos lignes résultantes et utiliser is_numeric () sur chacune. Quelque chose comme:

foreach($result as &$row){
 foreach($row as &$value){
  if(is_numeric($value)){
   $value = (int) $value;
  }       
 }       
}

Cela transformerait tout ce qui ressemble à un nombre en un ... certainement pas parfait.


Ne fonctionne pas pour les flotteurs. Ils réussissent le test is_numeric. Vous devez d'abord tester les flotteurs.
Martin van Driel

@MartinvanDriel ou toute chaîne contenant uniquement des nombres, mais qui devrait être une chaîne
treeface

@MartinvanDriel Essayez d'ajouter:if (is_float()) { $value = (float)$value; }
adjwilli

2

Dans mon projet, j'utilise généralement une fonction externe qui "filtre" les données récupérées mysql_fetch_assoc.

Vous pouvez renommer les champs de votre table afin de comprendre de manière intuitive quel type de données est stocké.

Par exemple, vous pouvez ajouter un suffixe spécial à chaque champ numérique: si useridc'est un, INT(11)vous pouvez le renommer userid_iou s'il s'agit d'un, UNSIGNED INT(11)vous pouvez le renommer userid_u. A ce stade, vous pouvez écrire une simple fonction PHP qui reçoit en entrée le tableau associatif (récupéré avec mysql_fetch_assoc), et appliquer le cast à la "valeur" stockée avec ces "clés" spéciales.


1

Si des instructions préparées sont utilisées, le type sera int le cas échéant. Ce code renvoie un tableau de lignes, où chaque ligne est un tableau associatif. Comme si fetch_assoc()était appelé pour toutes les lignes, mais avec des informations de type préservées.

function dbQuery($sql) {
    global $mysqli;

    $stmt = $mysqli->prepare($sql);
    $stmt->execute();
    $stmt->store_result();

    $meta = $stmt->result_metadata();
    $params = array();
    $row = array();

    while ($field = $meta->fetch_field()) {
      $params[] = &$row[$field->name];
    }

    call_user_func_array(array($stmt, 'bind_result'), $params);

    while ($stmt->fetch()) {
      $tmp = array();
      foreach ($row as $key => $val) {
        $tmp[$key] = $val;
      }
      $ret[] = $tmp;
    }

    $meta->free();
    $stmt->close();

    return $ret;
}

1

MySQL a des pilotes pour de nombreuses autres langues, la conversion des données en chaîne "standardise" les données et laisse à l'utilisateur le soin de taper des valeurs en int ou autres


1
Je crois que les types primitifs sont à peu près un "standard"? Si un pilote spécifique à une langue ne pouvait pas répondre aux besoins de cette langue, je dirais que c'est très décevant
Chung

1

Dans mon cas, l' mysqlnd.soextension avait été installée. MAIS je ne l'avais pas fait pdo_mysqlnd.so. Ainsi, le problème avait été résolu en remplaçant pdo_mysql.sopar pdo_mysqlnd.so.


0

J'aime la technique de mastermind , mais le codage peut être plus simple:

function cast_query_results($result): array
{
    if ($result === false)
      return null;

    $data = array();
    $fields = $result->fetch_fields();
    while ($row = $result->fetch_assoc()) {
      foreach ($fields as $field) {
        $fieldName = $field->name;
        $fieldValue = $row[$fieldName];
        if (!is_null($fieldValue))
            switch ($field->type) {
              case 3:
                $row[$fieldName] = (int)$fieldValue;
                break;
              case 4:
                $row[$fieldName] = (float)$fieldValue;
                break;
              // Add other type conversions as desired.
              // Strings are already strings, so don't need to be touched.
            }
      }
      array_push($data, $row);
    }

    return $data;
}

J'ai également ajouté la vérification des requêtes retournant false plutôt qu'un jeu de résultats.
Et vérifier une ligne avec un champ qui a une valeur nulle.
Et si le type souhaité est une chaîne, je ne perds pas de temps dessus - c'est déjà une chaîne.


Je ne prends pas la peine de l'utiliser dans la plupart des codes php; Je compte juste sur la conversion automatique de type de php. Mais si vous interrogez beaucoup de données, pour ensuite effectuer des calculs arithmétiques, il est judicieux de convertir en premier les types optimaux.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.