php
Version de php : à partir de la 5.3-10.
accueil |
console | VBScript |
PowerShell | php |
MySQL | documentation
| formation |
trucs et astuces |
exemples | glossaire
Généralités
La
gestion d'erreurs dans l'environnement php
Programmation de la gestion des erreurs dans un script php
Error Handler en environnement php
Journalisation des erreurs en environnement php
I - GENERALITES
Le langage php
(prononcez péachepé) a été créé en 1994 par un certain danois-canadien
génial nommé
Rasmus Lerdorf pour gérer son CV sur Internet. Php signifiait au départ
"Personal Home Page". Lorsqu'il mit ses sources à la disposition du public,
le php fut repris en 1997 par deux israéliens,
Andi Gutmans et
Zeev Suraski, cofondateurs de
ZEND.
La particularité
de php est que c'est un langage de programmation côté serveur
(comme ASP ou CGI ou JSP), contrairement à VBscript (ou C++ ou ce que
vous voulez) qui est un langage de programmation côté client (un équivalent
de VBScript côté client est Javascript, mais il peut être désactivé
dans un navigateur, contrairement à php). Une grosse différence entre
un langage de scripts côté serveur et ceux qui tournent sur un ordinateur
client réside dans le fait que, pour des raisons évidentes de sécurité, les
scripts côté serveur ne peuvent pas techniquement interagir sur la machine
qui les héberge. Leur seule mission est de construire des pages HTML et
d'accéder à des bases de données, la plus connue et la plus utilisée (côté
serveur) étant MySQL.
Petit détail :
on ne traite pas de PDO dans cette page. La programmation orientée objet
en PHP, on verra ça plus tard...
[début]
II - LA GESTION D'ERREURS DANS L'ENVIRONNEMENT php
Php
propose deux moyens de gérer les erreurs d'exécution (les erreurs de
compilation ne sont pas gérables et leur programmation ne présenterait aucun
intérêt puisque le script se "plante" dès qu'il en rencontre une).
Ce sont :
Exemple :
$fp =
@fsockopen('ssl://ipnpb.paypal.com', 443, $errno, $errstr, 30);
if ($errno != 0)
{
// ERREUR HTTP
echo "%NIP-F-SOCKOPEN, Paypal SSL HTTP error " . $errno . " " .
$errstr;
exit;
}
else
et
Exemple :
$fp =
@fsockopen('ssl://ipnpb.paypal.com', 443, $errno, $errstr, 30);
if (!$fp)
{
// ERREUR HTTP
echo "%NIP-F-SOCKOPEN, Paypal SSL HTTP error " . $errno . " " .
$errstr;
exit;
}
else
A première vue,
on dirait bien que l'on a programmé la même chose... et bien pas du tout. Il
peut y avoir des cas où un fsockopen (ou un pfsockopen) se
plante sans positionner la valeur de son $errno. La doc php dit : "Si
la valeur retournée par $errno est 0 et que la
fonction retourne FALSE, ce peut être une indication laissant penser
que l'erreur est survenue avant l'appel à connect().
La plupart du temps, cela est dû à un problème d'initialisation du socket."
(voir
ici).
Donc, c'est la
deuxième programmation qui est à privilégier.
La méthode
indirecte peut fait l'objet d'une programmation synchrone (via un
error handler programmé avec set_error_handler) ou
asynchrone (via un error handler exceptionnel utilisant
set_exception_handler. Voir plus loin).
On l'a déjà vu
dans les pages qui précèdent, la programmation synchrone consiste à tester
un "return status" (ici $fp)
et à faire ceci ou cela selon sa valeur (en général aller lire le dernier
code erreur renvoyé par le système), alors qu'un error handler
asynchrone démarre tout seul en cas de problème et exécute son propre code.
Quelques mots
sur le caractère "@" devant l'appel à la fonction. Ce caractère dit au
système de ne pas afficher lui-même l'erreur, sinon on aurait deux fois le
message, et le premier est loin d'être sympathique.
Jugez-en plutôt :
test_toto.php
(sans le gastéropode)
<?php
$fp = fsockopen('ssl://www.toto.com', 443, $errno, $errstr, 30);
if (!$fp)
{
// ERREUR HTTP
echo "%NIP-F-SOCKOPEN, SSL HTTP error " . $errno . " " . $errstr;
exit;
}
?>
Résultat
Warning: fsockopen() [function.fsockopen]:
unable to connect to ssl://www.toto.com:443 (Connection refused) in
/home/www/62585963bc9e2a5e92eba0113c714f19/web/vbscript/test_toto.php
on line 2
%NIP-F-SOCKOPEN, SSL HTTP error 111 Connection refused
test_toto.php
(avec le gastéropode)
<?php
$fp = @fsockopen('ssl://www.toto.com', 443, $errno, $errstr, 30);
if (!$fp)
{
// ERREUR HTTP
echo "%NIP-F-SOCKOPEN, SSL HTTP error " . $errno . " " . $errstr;
exit;
}
?>
Résultat
%NIP-F-SOCKOPEN, SSL HTTP error 111 Connection
refused
Mieux, non ?
[début]
III - PROGRAMMATION DE LA GESTION DES ERREURS DANS UN SCRIPT php
On ne voit pas comment on
pourrait mieux expliquer tout ce qu'il y a dans le manuel, donc, c'est
par
ici.
La seule chose à répéter, c'est
l'absolue nécessité de tester systématiquement tous les return status.
Exemple pour MySQL (que l'on verra plus en détails
ici) :
$db_server = "mysql.monsite.com";
$db_basename = "mabase";
$db_user = "root";
$db_pwd = "123456789";
$rs = mysql_connect($db_server, $db_user, $db_pwd);
if (!$rs)
{
echo "%NIP-F-SQLCONNECT, Connect error " . mysql_errno() . " " .
mysql_error();
exit;
}
$rs = mysql_select_db($db_basename);
if (!$rs)
{
echo "%NIP-F-SQLOPEN, Open error " . mysql_errno() . " " .
mysql_error();
exit;
}
$query = "SELECT * FROM utilisateurs WHERE idTransaction= '$txn_id'";
$rs = mysql_query($query);
if (!$rs)
{
$err = "%NIP-F-SQLQUERY_1, Query error " . mysql_errno() . " " .
mysql_error();
exit;
}
$data = mysql_fetch_object($query);
if
(!$data)
{
Le message avec
le code d'identification SQLQUERY_1 est très important. En effet,
dans le cas de la gestion de bases de données (ou de fichiers classiques,
mais ils sont de plus en plus rares), vous pouvez effectuer dans un même
source des dizaines d'opérations. Donc, avec un quantième dans le message
d'erreur, vous vous facilitez la vie pour retrouver la ligne qui a planté
dans votre source.
On verra dans la
page dédiée à MySQL la différence entre une
instruction qui se plante : if (!$rs)
et une instruction qui ne renvoie rien : if
(!$data).
[début]
IV - ERROR HANDLER EN ENVIRONNEMENT php
Idem que
ci-dessus, tout est dans la doc ! Mais il importe de différencier la
gestion synchrone de la gestion
asynchrone.
Prenons un
exemple :
Vous voulez
ouvrir une base sur une machine distante. Vous testez votre Open, et s'il ne
se passe pas comme prévu, vous exécutez un code de gestion d'erreur
correspondant. Ce sera dans ce cas une gestion synchrone : on agit,
il y a un problème, on réagit.
Mais si, pendant
une transaction, le lien réseau tombe, par exemple, il n'existe pas
d'instruction disant "tant que le lien est up je fais ceci, et quand il
est down je fais cela". Cette situation est gérée par ce que certains
appellent un error handler asynchrone ou exception handler. En
informatique, une exception est un événement imprévu, il ne peut donc
être programmé. D'où l'intérêt des procédures asynchrones. Tous les langages
évolués (sauf VBScript) offrent une
programmation asynchrone des erreurs.
set_error_handler
restore_error_handler
set_exception_handler
restore_exception_handler
Les erreurs renvoyées (ou non)
par php sont programmables avec la fonction
error_reporting
L'affichage
(ou non) des erreurs renvoyées par php se gère avec la directive
display_errors
qui se
positionne dans le php.ini
Et pour tout savoir sur la
gestion des erreurs en php,
c'est ici ! :-)
Remarque
Pour éviter
de donner un bâton pour se faire battre par le hacker de passage, il est
plus qu'évident que vous devez garder un contrôle absolu sur les
messages d'erreur qui s'affichent. Si votre serveur publie sur la page
de votre visiteur des messages comme : "impossible d'ouvrir le
fichier [path]my.ini, fichier en cours de mise à jour par un autre
process", vous cherchez la bagarre et c'est vous qui perdrez. Donc,
autant vous devez (à notre avis) être le plus exhaustif possible en cas
d'erreurs fatales (les autres, l'utilisateur n'a pas besoin de les
connaître), autant vous devez désactiver la diffusion automatique des
erreurs avec le paramètre display_errors
OFF.
|
[début]
V - JOURNALISATION DES ERREURS EN ENVIRONNEMENT php
Au risque de se répéter, rien de
mieux que la doc, car php comporte une fonction
error_log. Elle est pas belle, la vie ?
Cela dit, on peut préférer avoir
son propre journal d'événements. Voici donc un exemple :
a) Subroutine d'écriture dans
le journal (à mettre dans tous les scripts non interactifs)
//--------------------------------------------------------------------------------
function mylog($desc)
{
$logFile = "errlog.htm";
$ligne = "<br>" . date('Y-m-d H:i:s')." : $desc";
// ouverture du fichier de log, le mode "a+" permet d'écrire à la fin
if($fp = fopen($logFile, "a+"))
{
// écriture de la ligne à concurrence de 1024 caractères
fwrite($fp, $ligne, 1024);
// fermeture du fichier
fclose($fp);
}
}
L'intérêt d'ouvrir et de fermer
le fichier de log à chaque fois permet à d'autres scripts de faire pareil...
b) Exemple d'utilisation
$header = "POST /cgi-bin/webscr HTTP/1.0\r\n";
$header .= "Host: ipnpb.paypal.com:443\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
mylog ("ouverture socket réel SSL");
$fp = fsockopen ('ssl://ipnpb.paypal.com', 443, $errno, $errstr, 30);
if (!$fp)
{
// ERREUR HTTP
$err = "%NIP-F-SOCKOPEN, Paypal SSL HTTP error " . $errno . " " .
$errstr;
mylog ($err);
mylog ("Script interrompu anormalement.");
exit;
}
else
{
fputs ($fp, $header . $req);
while (!feof($fp))
{
$res = fgets ($fp, 1024);
mylog ("res = $res"); // debug
if (strcmp ($res, "VERIFIED") == 0)
{
mylog ("Notification validée
par Paypal"); // voir doc PP_OrderManagement_IntegrationGuide.pdf
c) Résultat (log réel d'un
script de notification de paiement Paypal)
Dans cet exemple, les deux
scripts appelés par Paypal en fin de paiement (le pdt et le nip)
écrivent dans le même journal :
errlog.htm
2012-04-02 18:33:59 : pdt.php - ===
pdt.php version 1.2-0. Début. ===
2012-04-02 18:33:59 : pdt.php - open real ssl
2012-04-02 18:34:00 : pdt.php - read data
2012-04-02 18:34:02 : pdt.php - HTTP/1.1 200 OK
2012-04-02 18:34:02 : pdt.php - Date: Mon, 02 Apr 2012 16:34:00 GMT
2012-04-02 18:34:02 : pdt.php - X-Frame-Options: SAMEORIGIN
2012-04-02 18:34:02 : pdt.php - Set-Cookie:
cwrClyrK4LoCV1fydGbAxiNL6iG=fpNm6GtXhKBsRHaofhebFUXn7ka2DVBoeJag8yrKPKw9IuNo2vfZbWfWYDbKyR3fBgRE9v_ccZ-aNkEhQrwu3ADCd9cOIDiOgs6p9lcYowiPtrSzH4AgN0ItuG%7cWYg3h76uVSILudm62UBlSEGwLt9j8_uny4e28vtnMUKEyXBa_2ax-1OYqLIW-eXJLmwQzW%7cjsHEznAx5CWfbm6RQCwcZL7cY76vEhsNLXLeSZF24OIzERQw5N8J6u0XW6UjWwSt5o8PKm%7c1333384441;
domain=.paypal.com;
path=/; Secure; HttpOnly
2012-04-02 18:34:02 : pdt.php - Set-Cookie:
KHcl0EuY7AKSMgfvHl7J5E7hPtK=DDGa1PCdR-fxuNwUSqSsnURS1SaMF34M233DvKkvhT
QxXiSKnl-bFncUOMPIVva0rE6IIAsRZT2dOtz7;
expires=Sun, 28-Mar-2032 16:34:01 GMT; domain=.paypal.com; path=/; Secure;
HttpOnly
2012-04-02 18:34:02 : pdt.php - Set-Cookie: cookie_check=yes; expires=Thu,
31-Mar-2022 16:34:01 GMT; domain=.paypal.com;
path=/; Secure; HttpOnly
2012-04-02 18:34:02 : pdt.php - Set-Cookie: navcmd=_notify-synch;
domain=.paypal.com; path=/; Secure; HttpOnly
2012-04-02 18:34:02 : pdt.php - Set-Cookie: navlns=0.0; expires=Sun,
28-Mar-2032 16:34:01 GMT; domain=.paypal.com; path=/;
Secure; HttpOnly
2012-04-02 18:34:02 : pdt.php - Set-Cookie:
Apache=10.74.8.47.1333384440879186; path=/; expires=Wed, 26-Mar-42
16:34:00 GMT
2012-04-02 18:34:02 : pdt.php - Vary: Accept-Encoding
2012-04-02 18:34:02 : pdt.php - Strict-Transport-Security: max-age=14400
2012-04-02 18:34:02 : pdt.php - Connection: close
2012-04-02 18:34:02 : pdt.php - Content-Type: text/html; charset=UTF-8
2012-04-02 18:34:02 : pdt.php - Set-Cookie:
TSe9a623=5501c5b9892f6bb098088c463413d9a796f03f3254fbcc334f79d4f8a0e24589192e4a1867835503f1634137d160814ba64fcf87682d8f7c;
Path=/
2012-04-02 18:34:02 : pdt.php -
2012-04-02 18:34:02 : pdt.php - SUCCESS
2012-04-02 18:34:02 : pdt.php - mc_gross=12.00
2012-04-02 18:34:02 : pdt.php - protection_eligibility=Ineligible
2012-04-02 18:34:02 : pdt.php - payer_id=AA9DAAAAACV36
2012-04-02 18:34:02 : pdt.php - tax=0.00
2012-04-02 18:34:02 : pdt.php -
payment_date=09%3A33%3A50+Apr+02%2C+2012+PDT
2012-04-02 18:34:02 : pdt.php - payment_status=Completed
2012-04-02 18:34:02 : pdt.php - charset=windows-1252
2012-04-02 18:34:02 : pdt.php - first_name=Didier
2012-04-02 18:34:02 : pdt.php - mc_fee=0.66
2012-04-02 18:34:02 : pdt.php - custom=N6qytUlc28
2012-04-02 18:34:02 : pdt.php - payer_status=verified
2012-04-02 18:34:02 : pdt.php - business=toto%40gmail.com
2012-04-02 18:34:02 : pdt.php - quantity=1
2012-04-02 18:34:02 : pdt.php - payer_email=tata%40gmail.com
2012-04-02 18:34:02 : pdt.php - txn_id=72001234567899443P
2012-04-02 18:34:02 : pdt.php - payment_type=instant
2012-04-02 18:34:02 : pdt.php - btn_id=43469576
2012-04-02 18:34:02 : pdt.php - last_name=Morandi
2012-04-02 18:34:02 : pdt.php - receiver_email=toto%40gmail.com
2012-04-02 18:34:02 : pdt.php - payment_fee=
2012-04-02 18:34:02 : pdt.php - shipping_discount=0.00
2012-04-02 18:34:02 : pdt.php - insurance_amount=0.00
2012-04-02 18:34:02 : pdt.php - receiver_id=AF8E123456K4W
2012-04-02 18:34:02 : pdt.php - txn_type=web_accept
2012-04-02 18:34:02 : pdt.php - item_name=Page+Web+JaiTrouveUnJob+perso
2012-04-02 18:34:02 : pdt.php - discount=0.00
2012-04-02 18:34:02 : pdt.php - mc_currency=EUR
2012-04-02 18:34:02 : pdt.php - item_number=
2012-04-02 18:34:02 : pdt.php - residence_country=FR
2012-04-02 18:34:02 : pdt.php - handling_amount=0.00
2012-04-02 18:34:02 : pdt.php - shipping_method=Default
2012-04-02 18:34:02 : pdt.php - transaction_subject=N6qytUlc28
2012-04-02 18:34:02 : pdt.php - payment_gross=
2012-04-02 18:34:02 : pdt.php - shipping=0.00
2012-04-02 18:34:02 : pdt.php - validation status is SUCCESS
2012-04-02 18:34:02 : pdt.php - payment status is Completed
2012-04-02 18:34:02 : pdt.php - connect to SQL server
2012-04-02 18:34:02 : pdt.php - connect to SQL database
2012-04-02 18:34:02 : pdt.php - update database
2012-04-02 18:34:02 : pdt.php - === end of processing ===
2012-04-02 18:34:04 : nip.php - === nip.php version v3.2-0. Début. ===
2012-04-02 18:34:04 : nip.php - lecture des data du Post
2012-04-02 18:34:04 : nip.php - Paypal Transaction ID = >7200123456443P<
2012-04-02 18:34:04 : nip.php - Paiement status = >Completed<
2012-04-02 18:34:04 : nip.php - Paiement date = >09:33:50 Apr 02, 2012
PDT<
2012-04-02 18:34:04 : nip.php - Buyer Last Name = >Morandi<
2012-04-02 18:34:04 : nip.php - Buyer First Name = >Didier<
2012-04-02 18:34:04 : nip.php - SQL unique ID = >N6qytUlc28<
2012-04-02 18:34:04 : nip.php - Paiement amount = >12.00<
2012-04-02 18:34:04 : nip.php - Paiement Currency = >EUR<
2012-04-02 18:34:04 : nip.php - Pending reason (if any) = ><
2012-04-02 18:34:04 : nip.php - Refund reason (if any) = ><
2012-04-02 18:34:04 : nip.php - Buyer email = >tata@gmail.com<
2012-04-02 18:34:04 : nip.php - Vendor email = >toto@gmail.com<
2012-04-02 18:34:04 : nip.php - ouverture socket réel SSL
2012-04-02 18:34:05 : nip.php - res = HTTP/1.1 200 OK
2012-04-02 18:34:05 : nip.php - res = Date: Mon, 02 Apr 2012 16:34:05 GMT
2012-04-02 18:34:05 : nip.php - res = Server: Apache
2012-04-02 18:34:05 : nip.php - res = X-Frame-Options: SAMEORIGIN
2012-04-02 18:34:05 : nip.php - res = Set-Cookie:
cwrClyrK4LoCV1fydGbAxiNL6iG=mySlc6iq3FnBdG9GCU6iUtp3L6gV0LzsCleYdcHa0ip5nSUf1q3arH3ZrYYG7WvztKWgK16lflt8bY5R-eoKsnFLjbkyj
GFkD469wm494-4R9QTwz7nkiRbYbtgtoxr3b1QFHm%7cE3fmytBiRZ1j9Wiy0TlohAexgnzW5UVlKs5B3zXv7rGUZUauOo8FMKiehpDYUbWCTsr7gW%7cIEg3S
4oNnH4-Bl20vffhR43c4D4mqBsDE2Qk9tyiHYhhSE_LL7y8l_pCrJ6ABzMRS5I_6m%7c1333384445;
domain=.paypal.com; path=/; Secure; HttpOnly
2012-04-02 18:34:05 : nip.php - res = Set-Cookie: cookie_check=yes;
expires=Thu, 31-Mar-2022 16:34:05 GMT; domain=.paypal.com;
path=/; Secure; HttpOnly
2012-04-02 18:34:05 : nip.php - res = Set-Cookie: navcmd=_notify-validate;
domain=.paypal.com; path=/; Secure; HttpOnly
2012-04-02 18:34:05 : nip.php - res = Set-Cookie: navlns=0.0; expires=Sun,
28-Mar-2032 16:34:05 GMT; domain=.paypal.com;
path=/; Secure; HttpOnly
2012-04-02 18:34:05 : nip.php - res = Set-Cookie:
Apache=10.73.8.52.1333384445273661; path=/; expires=Wed, 26-Mar-42
16:34:05
GMT
2012-04-02 18:34:05 : nip.php - res = Vary: Accept-Encoding
2012-04-02 18:34:05 : nip.php - res = Strict-Transport-Security:
max-age=14400
2012-04-02 18:34:05 : nip.php - res = Connection: close
2012-04-02 18:34:05 : nip.php - res = Content-Type: text/html;
charset=UTF-8
2012-04-02 18:34:05 : nip.php - res =
2012-04-02 18:34:05 : nip.php - res = VERIFIED
2012-04-02 18:34:05 : nip.php - Notification validée par Paypal
2012-04-02 18:34:05 : nip.php - connection au serveur SQL
2012-04-02 18:34:05 : nip.php - ouverture de la base
2012-04-02 18:34:05 : nip.php - payment status : completed
2012-04-02 18:34:05 : nip.php - execute query
2012-04-02 18:34:05 : nip.php - la base a été mise à jour
2012-04-02 18:34:05 : nip.php - === Fin normale du traitement ===
Croyez-en l'auteur, c'est un
outil in-con-tour-na-ble... :-)
Demain,
MySQL.
[début]
accueil |
console | VBScript |
PowerShell |
documentation | formation |
trucs et astuces |
exemples | glossaire
|