8800 sujets

Développement web côté serveur, CMS

Bonsoir Alsa ! Smiley smile

Je viens vous demander conseil, car j'ai un "léger" problème avec PDO. Mais voici un bout de code, tout d'abord :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">

<head>
	<title>Page d'essai</title>
	
	<meta http-equiv="content-language" content="fr" />
	<meta http-equiv="content-type" content="text/html; charset=utf-8" />
	
	<meta name="robots" content="noindex, nofollow" />
</head>

<body>
<?php
	define("DB_SERVER",	"locdalhost");
	define("DB_PORT",	"3306");
	define("DB_NAME",	"***");
	define("DB_USER",	"***");
	define("DB_PASSWORD",	"***");
	define("DB_PREFIX",	"***");
	
	try {
		$db = new PDO(
			"mysql:host=" . DB_SERVER . ";	dbname=" . DB_NAME, DB_USER, DB_PASSWORD);
	}
	catch(PDOException $e) {
		echo("\t" . $e->getMessage() . "\n");
	}
?>
</body>

</html>


J'ai volontairement mal écrit le nom du serveur, pour provoquer une erreur.

Lors du try/catch, getMessage() me retourne donc bien le message d'erreur (concernant que le serveur est introuvable). Le problème étant qu'il me retourne une chaîne encodée en ISO-8859, alors que mes pages sont encodées en UTF-8.

Cela fait près de trois heures que je fouille Google en espérant trouver une solution, sans succès. Je re-précise : mes pages sont BIEN encodées en UTF-8. Tout fonctionne correctement, sauf PDO.

Au cas où ça pourrait vous être utile, j'emploie PHP 5.3.0. Smiley cligne

Merci d'avance ! Smiley smile
connecté
Modérateur
Hello SolykZ,

a écrit :

[...]
Le problème étant qu'il me retourne une chaîne encodée en ISO-8859, alors que mes pages sont encodées en UTF-8.
[...]

N'étant pas un expert en encodage, je vais essayer quand même de t'aider...
Je ne crois pas que pdo change l'encodage.
la chaine en iso vient d'où ? l'exception ? la BDD ? Comment sont configurées tes tables (ISO-8859-1 | utf-8 ) ?

ps : On est d'accord que la liste des définitions de constantes ne se trouvent pas dans ce fichier ?
Modifié par Nolem (27 Oct 2009 - 23:59)
Bonsoir à vous deux. Smiley smile

@Nolem : La chaîne provient de l'exception. Mes tables (même si je doute qu'actuellement ça ait un rapport, dans la mesure où ma requête ne fait qu'établir une connexion, mais étant loin d'être expert je puis me tromper) sont en utf8_unicode_ci. Smiley smile A la base, non, les définitions de constantes ne proviennent pas de ce fichier. Le code posté ci-dessus est un essai "hors structure" histoire de m'assurer que je n'avais pas moi-même provoqué cette erreur au travers d'une fonction. Petite question toutefois : en quoi est-ce important ? Smiley smile

@jo_link_noir : bah...à tort, peut-être, c'était une déduction. Enfin, disons qu'en modifiant l'encodage de ma page via le menu ad-hoc de mon navigateur pour de l'ISO-8859, l'accentuation de la chaîne était correcte (au détriment du reste de ma page, évidemment). De plus, si j'applique d'abord la fonction utf8_encode(); à getMessage(); (sans modifier quoi que ce soit d'autre), la chaîne est correctement affichée.

Cependant, n'étant pas certain que ce comportement soit celui par défaut (à mon humble avis : non ; dans la mesure où si tel était le cas j'aurais probablement eu l'occasion, pendant mes quelques heures de Google'age, de trouver quelqu'un d'autre se posant la même question) et souhaitant à terme distribuer le script contenant ces lignes, j'hésite à procéder de la sorte. Sans parler du fait qu'à mon avis, ça relève plus de la bidouille qu'autre chose.

Je voudrais pouvoir comprendre de quoi il s'agit, histoire de pouvoir ensuite appliquer une solution générique qui ne pénaliserait pas l'un ou l'autre utilisateur du script.

Enfin, d'ores et déjà merci à vous deux de vous pencher sur mon problème. Smiley smile
connecté
Modérateur
jo_link_noir a écrit :

C'est une question débile j'en convient mais comment t'as su que l'encodage est en ISO-8859

Il n'y a pas de question débile. Smiley cligne En France du moins, il y a trois standards d'encodage :
* iso-8859-1
* iso-8859-15
* utf-8

La plupart des applications tournent en iso-8859-1.

SolykZ a écrit :


@Nolem : La chaîne provient de l'exception. Mes tables (même si je doute qu'actuellement ça ait un rapport, dans la mesure où ma requête ne fait qu'établir une connexion, mais étant loin d'être expert je puis me tromper) sont en utf8_unicode_ci. Smiley smile A la base, non, les définitions de constantes ne proviennent pas de ce fichier. Le code posté ci-dessus est un essai "hors structure" histoire de m'assurer que je n'avais pas moi-même provoqué cette erreur au travers d'une fonction. Petite question toutefois : en quoi est-ce important ? Smiley smile

@jo_link_noir : bah...à tort, peut-être, c'était une déduction. Enfin, disons qu'en modifiant l'encodage de ma page via le menu ad-hoc de mon navigateur pour de l'ISO-8859, l'accentuation de la chaîne était correcte (au détriment du reste de ma page, évidemment). De plus, si j'applique d'abord la fonction utf8_encode(); à getMessage(); (sans modifier quoi que ce soit d'autre), la chaîne est correctement affichée.

Cependant, n'étant pas certain que ce comportement soit celui par défaut (à mon humble avis : non ; dans la mesure où si tel était le cas j'aurais probablement eu l'occasion, pendant mes quelques heures de Google'age, de trouver quelqu'un d'autre se posant la même question) et souhaitant à terme distribuer le script contenant ces lignes, j'hésite à procéder de la sorte. Sans parler du fait qu'à mon avis, ça relève plus de la bidouille qu'autre chose.

Je voudrais pouvoir comprendre de quoi il s'agit, histoire de pouvoir ensuite appliquer une solution générique qui ne pénaliserait pas l'un ou l'autre utilisateur du script.

Enfin, d'ores et déjà merci à vous deux de vous pencher sur mon problème. Smiley smile


Je me doutais bien que c'était une page test. J'ai eu toutefois l'impression que tu avais mis volontairement les constantes login avec ton code d'exécution.

D'après ton premier post, j'avais bien l'impression que cela provenait soit de l'Exception ou soit de la BDD.
À propos de ton modèle Exception, l'as tu hérité ?

class MonException extends Exception {
//etc.
}


Si tel est le cas (ce que je doute fortement), tu encodes la valeur renvoyée à la méthode getMessage(). Si tu ne comprends pas bien ce que je viens de dire je te propose de lire ce qui suit.

Bien que la class Exception ne soit pas accessible afin de lire le code (écrite en C), on l'imagine à peu près comme ceci :

class Exception {
	private $message;
	private $code;
	private $line; // remplie auto lors de l'instanciation de l'objet
	private $file; // remplie auto lors de l'instanciation de l'objet

	final public function __construct($message,$code=null){
		$this->message = $message;
		$this->code = $code;
	}

	final public function getMessage(){
		return $this->message;
	}
	
	final public function getCode(){
		return $this->code;
	}

	final public function getFile(){
		return $this->file;
	}

	final public function getLine(){
		return $this->line;
	}	
}

<<<REEDITE

Donc, pour ton problème, il suffit d'étendre ton exception. Ce serait un truc du style :

class MonException extends Exception { 
    
    public function __construct($message){
        parent::__construct($message);
    } 
 
    public function getMessageUTF(){ 
        return utf8_encode($this->message); 
    }     
}



try{
    $var = "1";
    if($var === "1")
        throw new MonException("Hé hé, le beau message !");
        
}catch(MonException $e){
    echo $e->getMessageUTF();
}

REEDITE;
Modifié par Nolem (28 Oct 2009 - 14:21)
Merci de ta réponse Nolem ! Smiley smile

Aucun héritage d'exception, pour l'instant. C'est la première fois que je m'amuse avec de l'objet, et j'ai du mal à le digérer. Smiley lol

J'ai donc essayé ta class, et ... ahahah, attends je te poste le rendu :

a écrit :
Hé hé, le beau message !


L'exception, gérée cette fois par ma page PHP elle-même (qui est donc encodée en Unicode, pour rappel), fait foirer l'affichage des accents : le résultat d'utf8_encode(); sur une chaîne étant déjà en UTF-8.

Autre chose : j'ai testé ce fichier de test sur deux autres hébergements (ceux-là étant online). 123.fr ne gère apparemment pas encore la chose puisque le retour me dit que le driver est inconnu. Toile-Libre ne daigne pas me retourner quoique ce soit concernant le serveur. Et si j'introduis un nom d'utilisateur erroné, le retour est en anglais -> pas d'accents.

Je suis un adepte de la loi de l'emmerdement maximal. Smiley lol



Comme dit plus haut, j'avais bien essayé utf8_encode(getMessage());, et ça fonctionnait. Seulement, admettons que quelque part, ailleurs que chez moi (à mon grand regret), le retour de getMessage(); soit déjà de l'Unicode, l'utilisateur obtiendrait une chaîne ressemblant vaguement à "Hé hé, le beau message !". Puis, c'est quand même de la bricole.

Mais admettons que je n'aie aucun autre recours : puis-je utiliser ce genre de pratiques (je testerais une condition histoire d'être sûr de l'encodage de retour et de ne pas l'appliquer n'importe comment), ou c'est réellement à proscrire ?
Je reviens à la charge ! :-p

J'ai abandonné l'idée d'identifier la cause de l'erreur ; il serait possible que cela provienne du fait que mon système soit en français. Bref, j'ai finalement opté pour ta solution, Nolem, mais se pose maintenant un nouveau problème (je rappelle que je débute avec l'objet, j'ai essayé de copier au plus possible ta classe d'exemple ci-dessus mais sait-on jamais que j'aie loupé quelque chose) :

<?php
	class SOLBBException extends Exception
	{
		public function __construct($message)
		{
			parent :: __construct($message);
		}
		
		public function getMessageUTF()
		{
			return ((mb_detect_encoding($this->message, "UTF-8, ISO-8859-1, ISO-8859-15")) !== "UTF-8") ? utf8_encode($this->message) : ($this->message);
		}
	}
?>


Et au niveau de l'exception :

<?php
	try {
		$db = new PDO("mysql:host=" . DB_SERVER . "; port=" . DB_PORT . "; dbname=" . DB_NAME, DB_USER, DB_PASSWORD);
	}
	catch(SOLBBException $e) {
		echo($e->getMessageUTF);
	}
?>


J'obtiens malheureusement une erreur fatale :

a écrit :
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: H&#65533;te inconnu. ' in D:\Localhost\www\solbb\proceed\index.php:3 Stack trace: #0 D:\Localhost\www\solbb\proceed\index.php(3): PDO->__construct('mysql:host=locd...', 'root', '123456') #1 D:\Localhost\www\solbb\index.php(46): include('D:\Localhost\ww...') #2 {main} thrown in D:\Localhost\www\solbb\proceed\index.php on line 3


Ai-je omis quelque chose ?


(Tiens d'ailleurs, j'aurais une question (probablement bête, mais puisque je débute...) : j'ai voulu, en étendant la classe Exception, reprendre le nom de la méthode getMessage();, mais il semblerait que celle-ci soit finalisée. Existe-t-il un autre moyen me permettant de reprendre le même nom de méthode ? De la sorte, si mon problème est en réalité un bug (sait-on jamais), lorsque celui-ci sera corrigé il me suffira de supprimer ma classe étendue, laissant donc faire la classe originale. Smiley smile )
En fait je demandé comment t'as su pour l'encodage car normalement c'est une erreur en anglais alors sans accent '^^.

Mais sinon pour pas mettre des utf8_encode partout est gardé les mécanismes de PDO faut étendre PDO et redéfinir chaque méthode qui envoie une exception pour la relancé cette fois utf-8. Et étendre PDOStatement pour faire la même chose.

class PDOStatement_UTF8 extends PDOStatement
{
//code pour capturé et relancé les exceptions au format utf-8
}

class PDO_UTF8 extends PDO
{
    public function __construct($dsn, $username=NULL, $password=NULL, array $driver_options=NULL)
    {
        try{
            parent::__construct($dsn, $username, $password, $driver_options);
            //Définir une nouvelle classe qui remplace PDOStatement si PDOStatement doit lancé des exceptions
            $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('PDOStatement_UTF8'/*, array()*/));
			$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // pour retoutner des exception au lieu d'erreur
        }catch(PDOException $e){
			$e_utf8 = new PDOException(utf8_encode($e->getMessage()));
			$e_utf8->errorinfo = $e->errorinfo;
			throw $e_utf8;
        }
    }
//les autres méthode (query, etc)
}
Oui, sans les accents disons que ça serait passé complètement inaperçu, mais d'après les renseignements obtenus ça et là (j'avais posté mon problème à d'autres endroits), il semblerait, puisque getaddrinfo(); est une commande exécutée au niveau système, que le problème émane de Windows. N'étant pas un guru là-dedans, je ne peux ni confirmer ni infirmer. L'encodage serait donc en réalité de l'encodage DOS (cela dit, mb_detect_encoding($e->getMessage(), "UTF-8, ISO-8859-1, ISO-8859-15") me retourne qu'il s'agit d'ISO-8859-1, bref ce n'est quand même pas de l'UTF-8).

Je m'en vais de ce pas étudier ta classe, car je n'y ai rien compris. Smiley lol Smiley confused

Un grand merci à tous, en tout cas ! Smiley cligne
connecté
Modérateur
Hello,

Pourquoi utilises tu utf-8 ? tes données sont dans une langues différentes (Arabe, Russe) ?

Comme je t'ai écris l'extension, cela prouve bien que nous en sommes en utf-8. Bien que l'utf-8 gère pas mal de caractères, il ne gère pas les caractères latin.


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="fr" xml:lang="fr">
	<head>
		<meta http-equiv="content-type" content="text/html;charset=iso-8859-1" />
		<title>ok</title>
	</head>

	<body>
		<p>€ é é è </p>
	</body>
</html>



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="fr" xml:lang="fr">
	<head>
		<meta http-equiv="content-type" content="text/html;charset=utf-8" />
		<title>ok</title>
	</head>

	<body>
		<p>€ é é è </p>
	</body>
</html>


Pour revenir à ce que j'ai écris, cela implique :

class MonException extends Exception {  
     
    public function __construct($message){ 
        parent::__construct($message); 
    }  
  
    public function getMessageUTF(){  
        return utf8_encode(htmlentities($this->message));  
    }
} 


ps : Je ne connaissais pas mb_detect_encoding(), merci Smiley smile
Modifié par Nolem (30 Oct 2009 - 12:48)
Re-bonjour. Smiley smile

Je ne connaissais pas non plus, à vrai dire : il me fallait une solution permettant de trouver cette information, et je suis donc allé faire un tour sur Google. C'est très pratique, effectivement. Smiley smile

Concernant les retours en ISO-8859, il s'avère que c'est une erreur de configuration de mon serveur WAMP ; même si je n'ai toujours pas trouvé où. J'ai retourné tous les paramètres, rien (ou très peu) ne concerne les langues. Smiley smile Après coup, je me suis rendu compte que les informations retournées par getMessage(); n'intéresseront probablement pas les utilisateurs du script, je retourne donc une phrase explicative sans trop être technique, il me semble que c'est préférable. Smiley smile

Pour te répondre, j'emploie l'UTF-8 car je suis actuellement en train de coder un forum, et ne sachant pas à l'avance qui l'utilisera (vas savoir, peut-être qu'un russe l'aimera et souhaitera l'installer sur son serveur \o/), je préfère parer à cette éventualité. Smiley langue
Salut,

je n'ai pas tout lu parce que c'est un peu long mais voilà une citation Best Of :
Nolem a écrit :
Bien que l'utf-8 gère pas mal de caractères, il ne gère pas les caractères latin.
Arf ! Smiley langue

Relire d'urgence Comment bien déclarer l'encodage des caractères d'un document.

Quoi qu'il en soit la solution n'est certainement pas de rajouter des utf8_encode, utf8_decode et autre htmlentities à tour de bras.

En résumé pour avoir de l'utf8 :

* vérifier que si un encodage est déclaré au niveau des entêtes http il s'agit bien d'utf8

* déclarer de l'utf8 via une balise meta

* enregistrer ses documents en utf8 (sans BOM)

si on utilise une connexion à des tables il faut également :

* qu'elles soient encodées en utf8

* déclarer que la connexion est en utf8 en écrivant juste après la sélection de la base quelque chose comme :
mysql_set_charset('utf8', $link); 
ou
mysql_query('set names utf 8'); 
(à adapter à PDO que je n'utilise pas) .
Modifié par Heyoan (30 Oct 2009 - 20:07)
Bonsoir Heyoan. Smiley smile

Heyoan a écrit :
Quoi qu'il en soit la solution n'est certainement pas de rajouter des utf8_encode, utf8_decode et autre htmlentities à tour de bras.

En résumé pour avoir de l'utf8 :

* vérifier que si un encodage est déclaré au niveau des entêtes http il s'agit bien d'utf8
--> C'est fait.

* déclarer de l'utf8 via une balise meta
--> C'est fait.

* enregistrer ses documents en utf8 (sans BOM)
--> C'est fait.

si on utilise une connexion à des tables il faut également :
* qu'elles soient encodées en utf8
--> C'est le cas.
* déclarer que la connexion est en utf8 en écrivant juste après la sélection de la base
--> C'est le cas également. (pour PDO : $dbh->query("SET NAMES UTF8")).


Tout a été fait conformément aux règles. Le problème étant que le retour généré par getMessage(); est situé, il me semble, dans une bibliothèque écrite en C, donc compilée. Il m'est par conséquent difficile de la modifier pour qu'elle me renvoie de l'UTF-8. D'ailleurs, même si j'y arrivais, il faudrait que les utilisateurs de mon script en fassent de même, ce qui ne me mènera pas très loin.

J'espère qu'il existe une solution. Tout laisse à penser qu'il s'agit :

- soit d'un problème de configuration de mon serveur WAMP (j'ai laissé les paramètres par défaut, ayant installé Win 7 assez récemment j'ai installé WAMP puis commencé directement ce nouveau projet) ;
- soit d'un bug au niveau de cette bibliothèque.

J'en arrive à cette conclusion car une version antérieure de PHP (la dernière de la branche 5.2) me retourne les exceptions dans la langue de Shakespeare. Exit donc les problèmes d'accents, puisqu'il n'y en a pas.

En attendant une nouvelle version de PHP histoire de tirer une conclusion à ceci, je continue mon projet tel quel, car je suis persuadé que le problème n'émane pas de celui-ci. Smiley smile


Question : le sujet n'est pas réellement résolu mais je sais au moins dans quelle direction aller pour la suite. Dois-je donc le passer en résolu ou le laisser en attente et le modifier lorsque j'aurai eu le fin mot de l'histoire ? Smiley murf
Re',

effectivement les utilisateurs n'ont de toute façon pas à voir les résultats du getMessage ! Smiley cligne

Pour ce qui est du résolu à toi de voir... Smiley smile