Archives de catégorie : H – PHP et MySQL

Les différentes extensions PHP / MySQL

logo_php_assis.gif    logo_mysql1.gif

Le langage PHP est en pratique, quasiment indissociable de MySQL tant l’utilisation de ce tandem est répandue sur la toile. Tous les hébergeurs et fournisseurs d’accès Internet offrent ce service à leurs abonné(e)s.

Comment remplir une page Web avec des informations provenant d’une BD MySQL ?
En utilisant les extensions MySQL de PHP, c’est ce que nous allons étudier dans cette partie du cours.

PHP + MySQL permettent de développer des sites Internet au contenu totalement dynamique, et d’écrire des applications de gestion complètes sur le web, comme des applications de e-commerce par exemple.

Leur particularité par rapport à d’autres solutions techniques (.Net + SQLServer, Java + Oracle, …) est que cette solution est facile à mettre en place, Open Surce et donc gratuite !
MySQL peut être accédée en PHP de 3 manières différentes, grace :

  • à l’extension standard ‘mysql’ (ensemble de fonctions préfixées par ‘mysql_’)
  • à l’extension améliorée ‘mysqli’ ou Improved MySQL (ensemble de fonctions préfixées par ‘mysqli_’)
  • au pilote ‘PDO MySQL’ (ensemble de fonctions préfixées par ‘pdo_mysql’)

Elles seront vues en détail dans les chapitres suivants.

L’extension standard ‘ mysql ‘

Cette extension est souvent associée à la version basique du moteur MySQL. Elle est incluse en standard dans certains packages comme ‘easyPHP’ par exemple.
Elle utilise 3 ressources principales :

  • une connexion
  • un ordre SQL
  • un ensemble de lignes résultats (si le SQL est un SELECT bien sûr) : le ‘result set’ ou ‘curseur’

Du fait des limites de cette extension, les ordres SQL sont exécutés directement sans phase de préparation (parsing, bind),
– il n’y a pas de gestion explicite de transaction (commit, rollback). Les mises à jour seront par défaut AUTOCOMMITées

Voici d’après le site officiel PHP, la liste complète (une cinquantaine) des fonctions de l’extension ‘mysql’ en PHP5

L’extension ‘ mysqli ‘

Par rapport à la précédente :

  • supporté par MySQL à partir de 4.1.3 seulement
  • une approche objet des traitements (objet ‘mysqli’),
  • la gestion des transactions,
  • beaucoup plus de fonctions,
  • les ordres SQL préparés (avec bind variables),

soit plus de 110 fonctions, documentées sur http://fr.php.net/manual/fr/ref.mysqli.php

L’extension ‘ pdo_mysql ‘

pdo_mysql est un pilote spécifique pour MySQL, qui implémente l’interface de PHP Data Object. Comme son nom l’indique cette interface est orientée objet. La documentation est disponible sur http://fr.php.net/manual/fr/ref.pdo.php

A partir de la V5 de PHP, cette extension tend à remplacer pear_db.
C’est donc une couche d’interaction avec le SBGD (database layer) qui comme ODBC sur WIndows, permet d’écrire du code SQL portable, et est (relativement) indépendante du SGBD relationnel cible.

Les drivers suivants sont actuellement inclus :

PDO_MYSQL MySQL 3.x/4.x/5.x
PDO_DBLIB : pour FreeTDS / Microsoft SQL Server / Sybase
PDO_OCI : pour Oracle Call Interface
PDO_ODBC : pour ODBC v3 (IBM DB2 unixODBC et win32 ODBC
PDO_PGSQL : pour PostgreSQL
PDO_SQLITE : pour SQLite 3 et SQLite 2
PDO_FIREBIRD : pour Firebird/Interbase 6
PDO_IBM : pour IBM DB2
PDO_INFORMIX : pour IBM Informix Dynamic Server

PDO supporte les procédures stockées, les ordres préparés, les transactions,…

Logique générale d’un programme : CLI , PHP, MySQL et les autres

CLI et APIs standards

Le standard CLI (Call Level Interface) a été créé dans les années 90 par le SQL Access Group (SAG) pour permettre à tous les SGBDRs d’intéragir de manière normalisé avec des langages de programmation.

CLI est donc une interface standard de programmation : une ‘Application Programming Interface’ ou A.P.I

Pour + d’nfos voir : http://www.tar.hu/sqlbible/sqlbible0117.html

O.D.B.C (Open database Connectivity) de Microsoft est par exemple conforme à ce standard.

Concrètement cela veut dire que si l’on comprend la logique d’un programme PHP/mysqli, ou PHP/PDOMySQL, on pourra facilement écrire un programme en VB pour Oracle car la séquence des opérations et leur butssont quasiment toujours les mêmes…

Logique générale standard

La logique générale d’un programme accédant à une base de données (et exemple de fonctions CLI correspondantes) est la suivante :

1. allocation handler de connexion : AllocHandle()
2. connexion : Connect()
3. allocation handler ordre SQL : AllocHAndle()
4. (préparation éventuelle de l’ordre, si paramètres…) : Prepare()
5. exécution de l’ordre (voir zoom ci après) : Execute() ou ExecDirect()
6. déconnexion : Disconnect()
7. libération des ressources : FreeHAndle()

Zoomons plus précisément sur l’exécution d’un ordre :

cli_sql_flow.jpg

exemple d’application : mise à jour avec PDO

Un ordre de mise à jour simple sans paramètres

<?php

// ouverture de connexion
$dbh = new PDO(‘mysql:host=localhost;dbname=test’, $user, $pass);

// execution
$stmt->execute();

// liberation
$dbh = null;
?>

un ordre de mise à jour avec paramètres

<?php

// ouverture de connexion
$dbh = new PDO(‘mysql:host=localhost;dbname=test’, $user, $pass);

// preparation de l’ordre
$stmt =$dbh->prepare(« INSERT … »);

// association des parametres
$stmt->bindParam(…);
$stmt->bindParam(…);

// execution
$stmt->execute();

// liberation
$dbh = null;
?>

Un ordre SELECT est un peu plus compliqué car il faudra après l’exécution récupérer les lignes par une série de ‘fetch’ et bein sûr…les afficher en les formattant correctement.

Accès BD via l’extension mysql

Les fonctions de l’extension mysql

Voici d’après le site officiel PHP : http://www.php.net , la liste des fonctions de l’extension MySQL standard :

* mysql_affected_rows — Retourne le nombre de lignes affectées lors de la dernière opération MySQL
* mysql_change_user — Change le nom de l’utilisateur de la connexion active
* mysql_client_encoding — Retourne le nom du jeu de caractères utilisé par le client MySQL
* mysql_close — Ferme la connexion MySQL
* mysql_connect — Ouvre une connexion à un serveur MySQL
* mysql_create_db — Crée une base de données MySQL
* mysql_data_seek — Déplace le pointeur interne de résultat MySQL
* mysql_db_name — Lit les noms des bases de données
* mysql_db_query — Envoie une requête MySQL à un serveur MySQL
* mysql_drop_db — Efface une base de données MySQL
* mysql_errno — Retourne le numéro d’erreur de la dernière commande MySQL
* mysql_error — Retourne le texte associé avec l’erreur générée lors de la dernière requête
* mysql_escape_string — Protège les caractères spéciaux SQL
* mysql_fetch_array — Retourne une ligne de résultat MySQL sous la forme d’un tableau associatif, d’un tableau indexé, ou les deux
* mysql_fetch_assoc — Lit une ligne de résultat MySQL dans un tableau associatif
* mysql_fetch_field — Retourne les données enregistrées dans une colonne MySQL sous forme d’objet
* mysql_fetch_lengths — Retourne la taille de chaque colonne d’une ligne de résultat MySQL
* mysql_fetch_object — Retourne une ligne de résultat MySQL sous la forme d’un objet
* mysql_fetch_row — Retourne une ligne de résultat MySQL sous la forme d’un tableau
* mysql_field_flags — Retourne des détails sur une colonne MySQL
* mysql_field_len — Retourne la taille d’un champ de résultat MySQL
* mysql_field_name — Retourne le nom d’une colonne dans un résultat MySQL
* mysql_field_seek — Déplace le pointeur de résultat vers une position donnée
* mysql_field_table — Retourne le nom de la table MySQL où se trouve une colonne
* mysql_field_type — Retourne le type d’une colonne MySQL spécifique
* mysql_free_result — Libère le résultat de la mémoire
* mysql_get_client_info — Lit les informations sur le client MySQL
* mysql_get_host_info — Lit les informations sur l’hôte MySQL
* mysql_get_proto_info — Lit les informations sur le protocole MySQL
* mysql_get_server_info — Lit les informations sur le serveur MySQL
* mysql_info — Lit des informations à propos de la dernière requête MySQL
* mysql_insert_id — Retourne l’identifiant généré par la dernière requête INSERT MySQL
* mysql_list_dbs — Liste les bases de données disponibles sur le serveur MySQL
* mysql_list_fields — Liste les champs d’une table MySQL
* mysql_list_processes — Liste les processus MySQL
* mysql_list_tables — Liste les tables d’une base de données MySQL
* mysql_num_fields — Retourne le nombre de champs d’un résultat MySQL
* mysql_num_rows — Retourne le nombre de lignes d’un résultat MySQL
* mysql_pconnect — Ouvre une connexion persistante à un serveur MySQL
* mysql_ping — Vérifie la connexion au serveur MySQL, et s’y reconnecte au besoin
* mysql_query — Envoie une requête à un serveur MySQL
* mysql_real_escape_string — Protège les caractères spéciaux d’une commande SQL
* mysql_result — Retourne un champ d’un résultat MySQL
* mysql_select_db — Sélectionne une base de données MySQL
* mysql_set_charset — Sets the client character set
* mysql_stat — Retourne le statut courant du serveur MySQL
* mysql_tablename — Lit le nom de la table qui contient un champ
* mysql_thread_id — Retourne l’identifiant du thread MySQL courant
* mysql_unbuffered_query — Exécute une requête SQL sans mobiliser les résultats MySQL

Afin d’y voir plus clair, nous allons tenter de les regrouper par types d’opération ou de fonctionnalités

Taxinomie des fonctions MySQL de PHP

connexion mysql_connect, mysql_pconnect,
mysql_close, mysql_ping,
(my_sql_select_db, mysql_change_user)
gestion d’erreurs mysql_error, mysql_errno
méta données DB mysql_field_flags, mysql_field_name, mysql_field_len, mysql_field_table, mysql_field_type, mysql_list_fields, mysql_info,
mysql_insert_id,mysql__num_rows
mysql_list_tables, mysql_table_name
méta données Serveur mysql_client_encoding, mysql_get_client_info,
mysql_get_host_info, mysql_get_proto_info,
mysql_get_server_info,mysql_list_dbs,
mysql_list_processes, mysql_stat, mysql_thread_id
lecture lignes mysql_fetch_array, mysql_fetch_result_row,
mysql_fetch_assoc, mysql_fetch_field,
mysql_fetch_lengths, mysql_fetch_object, mysql_fetch_row,
execution SQL mysql_drop_db, mysql_db_query,
mysql_query, mysql_unbuffered_query

Se connecter et mettre à jour une base

Tout d’abord…se connecter

<?php
$dbhost = ‘mysql.estsurinternet.com:3306’;
$dbuser = ‘appli1’;
$dbpass = ‘mon_password’;
$conn = mysql_connect($dbhost, $dbuser, $dbpass) or die (‘Erreur de connexion à mysql’);
$dbname = ‘animal’;
mysql_select_db($dbname);
print (« Vous etes connecte à la base $dbname ! »);
?>
on pourra mettre la partie parametres de connexion dans un fichiers de configuration, qu’il n’ya aura qu’a inclure pour éviter de répéter le code :
<?php
// config_db.php
// usage : include ‘config_db.php’; …en début de programme
$dbhost = ‘localhost’;
$dbuser = ‘DD’;
$dbpass = ‘mon_password’;
$dbname = ‘animal’;
?>

Puis exécuter une commande SQL simple (autre que SELECT)

//une insertion de lignes
include ‘config_db.php’;
$conn = mysql_connect($dbhost, $dbuser, $dbpass)
or die (‘Erreur de connexion à mysql’);
mysql_select_db($dbname);
$ordre_sql = « INSERT INTO pet VALUES (…) »;
//implicitement mysql_query utilise la connexion précédente
mysql_query($ordre_sql) or die(‘Erreur insertion’);
// l’ordre est autoCOMMITé

rem : un SELECT est toujours un peu plus compliqué à gérer, quelquesoit le langage de programmation utilisé. En effet il faudra rapatrier les lignes …et les afficher !

SELECT avec fetch ligne à ligne

on se connecte , on définit un ordre SQL, et on fait des des fetch pour rapatrier les lignes…
Chaque ligne est un tableau de colonnes indicé numériquement

<?php
// connexion
$connection = mysql_connect(« localhost », »dd », »dd »);
mysql_select_db(« test », $connection);
// execution de la requete
$result = mysql_query (« SELECT name, continent FROM
country order by 2,1 », $connection );
// lecture des lignes
while ($row = mysql_fetch_row($result))   {

print (« nom : $row[0] continent : $row[1] <br> »);
}
// deconnexion
mysql_close($connection);
?>

Si on veut pouvoir afficher un SELECT quelconque sans connaitre les colonnes de la table, on fera une boucle interne pour lire toutes les colonnes.
On s’appuiera sur  la meta données ‘mysql_num_fields’ qui renvoie le nombre de colonnes de la table.

<?php
// connexion
$connection = mysql_connect(« localhost », »dd », »dd »);
mysql_select_db(« test », $connection);
// execution de la requete
$result = mysql_query (« SELECT name, continent FROM
country order by 2,1 », $connection );
// lecture des lignes
while ($row = mysql_fetch_row($result))   {
print (« <pre> »);
for ($i=0; $i<mysql_num_fields($result); $i++) {
print (« $row[$i] t »);
}
print (« n »);
}
print (« </pre> »);
// deconnexion
mysql_close($connection);
?>

et en rajoutant les entêtes  de colonne puisées dans  ‘mysql_field_name’ :

<?php
// connexion
$connection = mysql_connect(« localhost », »ddelegli », »ddelegli »);
mysql_select_db(« test », $connection);
// execution de la requete
$result = mysql_query (« SELECT name, continent FROM
country order by 2,1 », $connection );
// affichage des entetes
print (« <pre> »);
for ($i=0; $i<mysql_num_fields($result); $i++) {
print (mysql_field_name($result,$i). » t »);
}
print (« n »);
// lecture des lignes
while ($row = mysql_fetch_row($result))   {
for ($i=0; $i<mysql_num_fields($result); $i++) {
print (« $row[$i] t »);
}
print (« n »);
}
print (« </pre> »);
// deconnexion
mysql_close($connection);
?>

Exécution d’une procédure stockée avec mysqli

Créons une procédure stockée qui fait un simple SELECT sur une table .

Cette procédure ne prend pas de paramètres en entrée, maisi ramène n lignes, c’est là que ca se complique…

MYSQL> create procedure ps1 ()
begin
select * from dept;
end;

rem : la table DEPT contient 4 lignes de 3 colonnes…

Voici le code PHP correspondant qui lit le ‘RESULT SET’ (tableau de résultats) du SELECT excéuté par la procédure :

<?php
// connexion
$connexion = mysqli_connect(« localhost », « dd », « dd », « test »);

/* erreur eventuelle de connexion */
if (mysqli_connect_errno()) {
printf(« Pb lors de la connexion : %s<br> », mysqli_connect_error());
exit();
}
print (« connecte !<br><br> »);

/* appel de la procedure stockée */
$ordre_sql = « call ps1(); »;

if (mysqli_multi_query($connexion, $ordre_sql)) {
do {
// lecture d’une ligne…
if ($result = mysqli_store_result($connexion)) {
// affichage des 3 colonnes
while ($ligne = mysqli_fetch_row($result)) {
printf(« %s %s %s <br> », $ligne[0], $ligne[1],$ligne[2] );
}
mysqli_free_result($result);
}
// tant qu’il y en a…
} while (mysqli_next_result($connexion));
}

/* Fermeture de la connexion */
mysqli_close($connexion);
?>

accès BD avancés avec mysqli

Ordres SQL avec paramètres d’entrée
(prepared statements)

un insert simple paramétré

<?php
$conn = mysqli_connect(‘localhost’, ‘dd’, ‘dd’, ‘test’);

$stmt = mysqli_prepare($conn, « INSERT INTO CountryLanguage (countrycode, language) VALUES (?, ?) »);
// on associe les variables PHP aux parametres de l’ordre SQL
// il y en a 2 de type string (‘ss’)
mysqli_stmt_bind_param($stmt, ‘ss’, $code, $lang);

// affectation des valeurs de parametre (via les var php)
$code = ‘DEU’; $lang = ‘Bavarian’;

/* execute le prepared statement */
mysqli_stmt_execute($stmt);

print(« Nb de lignes traitees : « .mysqli_affected_rows($conn));

/* fermeture ordre sql */
mysqli_stmt_close($stmt);

/* fermeture connexion */
mysqli_close($conn);
?>

un select paramétré

<?php
$link = mysqli_connect(« localhost », « my_user », « my_password », « world »);
/* Vérification de la connexion */
if (mysqli_connect_errno()) {
printf(« Échec de la connexion : %sn », mysqli_connect_error());
exit();
}
$city = « Paris »;
/* Crée une requête préparée */
if ($stmt = mysqli_prepare($link, « SELECT District FROM City WHERE Name=? »)) {
/* association des variables*/
mysqli_stmt_bind_param($stmt, « s », $city);
/* Exécution de la requête */
mysqli_stmt_execute($stmt);
/* Lecture des variables résultantes */
mysqli_stmt_bind_result($stmt, $district);
/* Récupération des valeurs */
mysqli_stmt_fetch($stmt);
printf(« %s est dans le district de %sn », $city, $district);
/* Fermeture du traitement */
mysqli_stmt_close($stmt);
}
/* Fermeture de la connexion */
mysqli_close($link);
?>

SELECT avec un nombre queLconque de colonnes…
(extrait du manuel PHP, contribution de Matty : http://fr3.php.net/manual/en/function.mysqli-stmt-bind-result.php)

<?php
# of fields in result set.
$nof = mysqli_num_fields( mysqli_stmt_result_metadata($handle) );

# The metadata of all fields
$fieldMeta = mysqli_fetch_fields( mysqli_stmt_result_metadata($handle) );

# convert it to a normal array just containing the field names
$fields = array();
for($i=0; $i < $nof; $i++)
$fields[$i] = $fieldMeta[$i]->name;

# The idea is to get an array with the result values just as in mysql_fetch_assoc();
# But we have to use call_user_func_array to pass the right number of args ($nof+1)
# So we create an array:
# array( $stmt, &$result[0], &$result[1], … )
# So we get the right values in $result in the end!

# Prepare $result and $arg (which will be passed to bind_result)
$result = array();
$arg = array($this->stmt);
for ($i=0; $i < $nof; $i++) {
$result[$i] =  »;
$arg[$i+1] = &$result[$i];
}

call_user_func_array (‘mysqli_stmt_bind_result’,$arg);

# after mysqli_stmt_fetch(), our result array is filled just perfectly,
# but it is numbered (like in mysql_fetch_array() ), not indexed by field name!
# If you just want to mimic that ones behaviour you can stop here 🙂

mysqli_stmt_fetch($this->stmt);

# Now you can use $result
print_r($result);

# But beware! when using the fetch in a loop, always COPY $result or else you might
# end with all the same values because of the references
?>

Select simple PDO/Mysql

Voici le code :

<?php
// select simple avec PDO, vers MySQL
print (‘<link rel= »stylesheet » href= »gris.css »>’);

try {
$db=’test’; $user=’dd’; $mdp=’dd’;
$pdo = new PDO(« mysql:host=localhost;dbname=$db », $user, $mdp);

$ordre = $pdo->query(‘SELECT * FROM employes’);
$nbcol = $ordre->columnCount();

//lecture entetes – non supportee ??
// $nomcol= $ordre>getColumnMeta(« name »);
// print(« nom: $nomcol »);

print(« <table> »);
// lecture lignes
while ($row = $ordre->fetch(PDO::FETCH_NUM)){
print(« <tr> »);
// lecture colonnes
for ($j=0;$j < $nbcol;$j++) {
print (« <td> $row[$j] </td> »);
}
print(« </tr> »);
}
print(« </table> »);
} catch (PDOException $e) {
echo « Pb !! :  » . $e->getMessage() . « n »;
exit;
}

?>

note : apparemment ‘getColumnMeta’ n’est pas supporte par le driver MySQL livré avec Wampserver 5…

Exercice PHP / PDO – ordre SQL paramétré

Ecrire un programme PHP, utilisant l’extension PDO pour MySQL, qui lit les noms et salaires des employes d’un departement passé en paramètre.

Ce no de département  sera à la fois un paramètre du  programme PHP (passé dans l’URL par exemple) et un paramètre de l’ordre SELECT.
On utilisera un boucle de fetch simple, et on se limitera aux 2 colonnes demandées.

Corrigé PDO – ordre SQL paramétré

Voici un exemple de solution :

<?php

// recup du parametre d’entree (URL ou formulaire)
$no_dep =$_GET[‘no_dep’];

// variable du programme
$chaine_sql =’SELECT ename, sal FROM emp WHERE deptno =:no_dep’;
$user=’DD’; $mdp=’DD’;

try {

$connexion = new PDO(‘mysql:host=localhost;dbname=test’, $user, $mdp);

$ordre_sql = $connexion->prepare($chaine_sql);
// association param d’entree SQL et var PHP (bind)
$ordre_sql->bindParam(‘:no_dep’, $no_dep, PDO::PARAM_INT);

// execution de l’ordre
$ordre_sql->execute();
print (« <PRE> »);

// boucle de fetch
while ($ligne = $ordre_sql->fetch(PDO::FETCH_NUM, PDO::FETCH_ORI_NEXT)) {
$data = $ligne[0] . « \t\t » . $ligne[1] . « \n »;
print $data;
}
print (« </PRE> »);

// fermenure de la connexion
$connexion = null;

} catch (PDOException $e) {
print « Erreur!:  » . $e->getMessage() . « <br/> »;
die();
}

?>

Le résultat aura l’allure suivante :

Employes et salaires du departement 30

ALLEN		2342.56
WARD		2512.5
MARTIN		1512.5
BLAKE		3448.5
TURNER		1815
JAMES		1149.5

On aurait pu rajouter les nom de colonnes en utilisant la fonction getColumnMeta, mais celle ci est assez mal supportée pour le moment, d’après la doc…