begin process at 2010 03 21 17:08:04
  Trouver un code source :
 
dans
 
Accueil > 

Code

 > 

Class et Objet ( POO )

 > PHP5 - CLASSE DE GESTION DE SESSION

PHP5 - CLASSE DE GESTION DE SESSION


 Information sur la source

Note :
9,5 / 10 - par 8 personnes
9,50 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10
Catégorie :Class et Objet ( POO ) Classé sous :php5, singleton, session, sql, abstraite Niveau :Initié Date de création :29/05/2006 Date de mise à jour :01/06/2006 20:13:22 Vu :19 726

Auteur : FhX

Ecrire un message privé
Commentaire sur cette source (15)
Ajouter un commentaire et/ou une note

 Description

Huhu, bon ca faisait longtemps que j'avais pas posté un ptit code alors voila :)

Déja, avec un titre aussi compliqué (quoi que), je vais essayer de m'exprimer en bon français.
Tout le monde connait les sessions. On commence par appeler session_start(), et on utilise $_SESSION[] par la suite.
Petit rappel de fonctionnement, les données de sessions sont stockées sur le disque dur du serveur dans un fichier pour chaque visiteur.

Cependant, pour une envie comme une autre on désire utiliser un système de base de donnée au lieu d'un système via fichier. On pourrait écrire une classe qui fasse ca avec un parsing comme je l'avais fait il y a bien longtemps (que je n'ai jamais fini). Cependant, le procédé est bien trop lourd.

Comment faire pour mixer performance d'une base de donnée avec la performance des sessions sans tout ré-écrire ? Via session_set_save_handler().
Il suffit de recréer les méthodes dont on a besoin pour les faires intéragir avec une base de donnée plutôt qu'avec un système de fichier.
Et grâce à tout ca, on peut utiliser session_start, $_SESSION[] et toutes les fonctions session_*.

Pour pouvoir utiliser correctement l'approche objet de PHP5, j'ai découpé ce projet en 2.
La première partie est une classe abstraite. Toute classe de session DOIT être dérivée de cette classe abstraite. Elle contient les méthodes nécessaires au bon fonctionnement des sessions. (ainsi que les propriétés utiles pour chaque classe dérivée).
La deuxième partie est une classe concrète. Ici, j'ai choisi de faire une classe concrète de base de donnée. Je reprend donc les méthodes abstraites pour les compléter comme prévu.

On peut utiliser n'importe quel système pour les sessions, que ce soit un gestionnaire LDAP, une base de donnée SQL, nu système de fichier bref... il suffit pour cela de dériver la classe abstraite et de remplir les méthodes.


Voici donc ce que ca donne.

Source

  • <?php
  • abstract class SessionManager {
  • public $session_name;
  • public $lifeTime = 3600;
  • public $id;
  • abstract public function open($savePath, $sessName);
  • abstract public function close();
  • abstract public function read($sessID);
  • abstract public function write($sessID,$sessData);
  • abstract public function destroy($sessID);
  • abstract public function gc($sessMaxLifeTime);
  • public function __construct() {
  • if ( !session_set_save_handler(array(&$this,'open'),
  • array(&$this,'close'),
  • array(&$this,'read'),
  • array(&$this,'write'),
  • array(&$this,'destroy'),
  • array(&$this,'gc') ) ) {
  • throw new Exception('Erreur lors de l\'init des sessions !');
  • }
  • session_start();
  • }
  • }
  • class SQLSessionManager extends SessionManager {
  • private $db;
  • public function __destruct() {
  • session_write_close();
  • }
  • public function open($savePath, $sessName) {
  • $this->db = mysql::GetInstance();
  • return true;
  • }
  • public function close() {
  • $this->gc(ini_get('session.gc_maxlifetime'));
  • unset($this->db);
  • }
  • public function read($sessID) {
  • $this->id = $sessID;
  • $this->db->prepare("SELECT data FROM session WHERE id = '{1}' AND expires > {2}",
  • $sessID, time() );
  • $this->db->query();
  • return ( ( $row = $this->db->fetch_row() ) !== FALSE ) ? $row[0] : ' ';
  • }
  • public function write($sessID,$sessData) {
  • $this->id = $sessID;
  • $newExp = time() + $this->lifeTime;
  • $this->db->prepare("INSERT INTO session (id, data, expires) VALUES ('{1}', '{2}', {3})
  • ON DUPLICATE KEY UPDATE data = '{2}', expires = {3}",
  • $sessID, $sessData, $newExp);
  • $this->db->query();
  • return TRUE;
  • }
  • public function destroy($sessID) {
  • $this->db->prepare("DELETE FROM session WHERE id = '{1}'", $sessID);
  • $this->db->query();
  • return ( $this->db->affected_rows() === 1 ) ? TRUE : FALSE;
  • }
  • public function gc($sessMaxLifeTime) {
  • $this->db->prepare("DELETE FROM session WHERE ( UNIX_TIMESTAMP(expires) - UNIX_TIMESTAMP(NOW()) ) > '{1}' ", $sessMaxLifeTime);
  • $this->db->query();
  • return $this->db->affected_rows();
  • }
  • }
  • ?>
<?php
abstract class SessionManager {

 public $session_name;
 public $lifeTime = 3600;

 public $id;
 
 abstract public function open($savePath, $sessName);
 abstract public function close();
 abstract public function read($sessID);
 abstract public function write($sessID,$sessData);
 abstract public function destroy($sessID);
 abstract public function gc($sessMaxLifeTime);	

 public function __construct() {
 	
  if ( !session_set_save_handler(array(&$this,'open'),
                                 array(&$this,'close'),
                           		 array(&$this,'read'),
                          		 array(&$this,'write'),
                          		 array(&$this,'destroy'),
                          		 array(&$this,'gc') ) ) {
       throw new Exception('Erreur lors de l\'init des sessions !');
  }

  session_start();
 	
 }
}

class SQLSessionManager extends SessionManager {

 private $db;
 
 public function __destruct() {
 	session_write_close();
 }
 
 public function open($savePath, $sessName) {
 	
  $this->db = mysql::GetInstance();	
  return true;
 	
 }

 public function close() {
 	
  $this->gc(ini_get('session.gc_maxlifetime'));
  unset($this->db);
  
 }

 public function read($sessID) {
 	
  $this->id = $sessID;
  $this->db->prepare("SELECT data FROM session WHERE id = '{1}' AND expires > {2}",
                     $sessID, time() );
  $this->db->query();

  return ( ( $row = $this->db->fetch_row() ) !== FALSE ) ? $row[0] : ' ';
	
 }
 
 public function write($sessID,$sessData) {

  $this->id = $sessID;
  $newExp = time() + $this->lifeTime;
  $this->db->prepare("INSERT INTO session (id, data, expires) VALUES ('{1}', '{2}', {3})
                      ON DUPLICATE KEY UPDATE data = '{2}', expires = {3}",
   	                  $sessID, $sessData, $newExp);
  $this->db->query();
  
  return TRUE;
 	
 }
 
 public function destroy($sessID) {

  $this->db->prepare("DELETE FROM session WHERE id = '{1}'", $sessID);
  $this->db->query();
  return ( $this->db->affected_rows() === 1 ) ? TRUE : FALSE;
  	
 }
 
 public function gc($sessMaxLifeTime) {

  $this->db->prepare("DELETE FROM session WHERE ( UNIX_TIMESTAMP(expires) - UNIX_TIMESTAMP(NOW()) ) > '{1}' ", $sessMaxLifeTime);
  $this->db->query();
  return $this->db->affected_rows();
 	
 }
 
}
?>

 Conclusion

Et voila. Un gros merci à la doc de PHP la dessus sur lequel j'ai pris exemple.
Je compte rajouter de nouveaux trucs, mais la base est la en tout cas.
Si ca peut aider quelqu'un tant mieux :)

J'ai oublié, mes méthodes de singletons peuvent se retrouver dans la classe abstraite ca ne posera aucun problème :)


 Historique

30 mai 2006 00:20:28 :
Update, avant ca marchait pas. Maintenant oui :)
01 juin 2006 20:13:22 :
Remplacement du code SQL via ON DUPLICATE KEY - le gc qui marchait pas bien (je compte lui faire quelque chose de nouveau à ce machin, ca me plait pas encore) - mais j'ai oublié le singleton :p

 Sources du même auteur

PHP5 - UTILISATION DES ITÉRATEURS ET DE LA RÉFLECTION.
PHP 5 - CLASSE MYSQL (STYLE MYSQLI)
[PHP5] CLASSE DATE/HEURE
PHP5 - GESTION DE MODULES EN OBJET
PHP5 - CLASSE DE NEWS ET DE COMMENTAIRES

 Sources de la même categorie

Source avec Zip CLASSE TABLE HTML DYNAMIQUE par LDDL
Source avec Zip GÉNÉRATEUR DE COUCHE DAO POUR SITE WEB À PARTIR D'UNE BASE D... par alexfool
Source avec Zip [PHP5.2] CLASSE PDO par hornetbzz
Source avec Zip POO - LOGGING PACKAGE par Waredan
POO - OBJECT CLASS par Waredan

 Sources en rapport avec celle ci

Source avec Zip Source avec une capture CRÉER ET ETABLIR UNE PAGE DE MAINTENANCE POUR X HEURES par popolino0
Source avec Zip [PHP5.2] CLASSE PDO par hornetbzz
PHP5 - INSTANCIATION UNIQUE DE CLASSE VIA MULTITON par FhX
Source avec Zip PHP5 - COUCHE D'ABSTRACTION SGBD par FhX
Source avec Zip [PHP 5] CLASSE SQL par FhX

Commentaires et avis

Commentaire de malalam le 29/05/2006 15:28:04 administrateur CS

Hello,

c'est beau :-)
Et très utile. Bonne classe :-) Rien à redire, personnellement.

Commentaire de kankrelune le 29/05/2006 16:01:28

Hello... bonne classe bien écrite... .. .

Petit bémol au niveau du gc... je ne suis pas sur qu'il soit utile de le faire à chaque fermeture en outre la variable $sessMaxLifeTime que tu lui passe en paramètre ne sert à rien... .. .

A part ça de ce que j'ais vu c'est du bon boulot... .. . ;o)

@ tchaOo°

Commentaire de FhX le 29/05/2006 16:10:32

Oh voui, jfais passer des paramètres qui ne servent à rien :)

Quel gland des fois :p

Pour le gc, je sais plus, jvais revoir ca de plus près. Merci de m'y avoir fait penser.

Commentaire de juki_webmaster le 29/05/2006 19:04:00

Je prefere la methode sans session tout court, j'en ait fait une assez complexe, et franchement pour les gens anti-cookies sa vaut le coup ! (au passage je url rewritise le sessid et l'id membre pour les gens amoureux du cache).

Ce que tu devrais faire:
Autoriser exemple: 10 sessions / IP simultanée, suffisent non ?
Au lancement de la class effacer toutes les sessions expirer.

Si cette methode te plait, envisage-là car sa pourais tres bien la faire avec ta class.

Jolie source :)

Commentaire de kankrelune le 29/05/2006 19:21:41

[quote]Je prefere la methode sans session tout court, j'en ait fait une assez complexe, et franchement pour les gens anti-cookies sa vaut le coup ! (au passage je url rewritise le sessid et l'id membre pour les gens amoureux du cache).[/quote]

Alors là je trouve que tu te contredis... d'une par ce n'est pas parce que l'internaute n'accepte pas les cookies que tu ne pourra pas utiliser les sessions ensuite tu dis que tu utilise une methode sans sessions mais que tu réécris les url avec l'id de session... .. .

[quote]Ce que tu devrais faire:
Autoriser exemple: 10 sessions / IP simultanée, suffisent non ?
[/quote]

Soit j'ais rien compris à ce que tu viens de dire soit je vois pas l'interet de limiter le nombre de sessions... en gros tu veux limiter le nombre de visiteurs à ton site... et il fait quoi le 11 eme internaute qui veut se connecter à ton site... il doit attendre que les autres on finit... .. .

Ô_o

[quote]Au lancement de la class effacer toutes les sessions expirer.[/quote]

C'est ce qu'il fait dans la méthode close... à l'instantiation ou à la fermeture ça ne change rien... cependant je persiste à dire qu'il n'est pas utile de le faire à chaque page ,hormis pour des gros gros sites qui de par le nombre de visiteurs générent des paquets de sessions, mieux vaut développer une methode pour lancer le gc de manière périodique mais pas à chaque fois... ne serait ce qu'un...

if(time()%2)
  $this->gc();

Ca fait toujours ça de requêtes en moins... c'est pas un point crucial mais c'est toujours ça de gagné... .. . ;o)

@ tchaOo°

Commentaire de stepibou le 29/05/2006 23:45:33

Salut,
bonne source , meme si je trouve que ca resemble plus à un tuto,... En effet on retrouve une grande majorité de ton code sur la page de la fct session_set_save_handler() sur fr.php.net (en comptant les commentaires des membres)

Bon, moi, je me suis vraiment pris la tete pendant des heures et des heures , pour esayer de comprendre les sessions un peu plus en detail.
Et tout ca pour rien car je viens de voir que sous windaube l'expiration de session est mal géré... (notament en utilisant l'osolete ie) . Voir ici:
http://www.manuelphp.com/php/ini.session.gc-maxlifetime.php

Sinon, pour les sessions sauvés dans les fichiers :
il faudrait gérer le : session_save_path (rep de sauvegarde)

Voila bon code!
a+

ps: lol, ca faisait longtemps que t'avait pas poster un code. quoi 17 jours , 138min et 47s ??  !! mdr

Commentaire de FhX le 30/05/2006 00:17:06

Bah ca faisait un ptit moment vi :)
Ouais je sais que ca ressemble beaucoup à la doc de php, en même temps y'a pas 36 solutions pour implémenter un truc pareil lol =)

Ce que j'implémente en plus, c'est l'abstraction pour le moment. Je vais voir de nouvelles possibilitées à cette classe un peu plus tard.

J'update également, parce que ce que je vous ai mis n'est pas fonctionnel :)
Maintenant ui (testé et approuvé :))

Commentaire de Anthomicro le 30/05/2006 10:07:06

Salut,

j'aime bien ton script, c'est bien codé, mais qu'est-ce que c'est gourmand :-( j'imagine pas un site un tant soit peu visité fonctionner longtemps en mutu avec ton système, t'imagines le nombre de requêtes ?

Ce code-ci pourrait être optimisé :

# // Ecriture des données de session.
# public function write($sessID,$sessData) {
#  
# $this->id = $sessID;
# $newExp = time() + $this->lifeTime;
# $this->db->prepare("SELECT COUNT(id) FROM session WHERE id = '{1}'", $sessID);
# $data = $db->fetch_row();
#  
# if ( (int) $data[0] !== 0 ) {
#      $this->db->prepare("UPDATE session SET expires = {1}, data = '{2}' WHERE id = '{3}'",
#      $newExp, $sessData, $sessID);
#      if ( $this->db->affected_rows() === 1 ) {
#           return TRUE;
#      }
# } else {
#      $this->db->prepare("INSERT INTO session (id, data, expires) VALUES ('{1}', '{2}', {3})",
#      $sessID, $sessData, $newExp);
#      if ( $this->db->affected_rows() === 1 ) {
#           return TRUE;
#      }
# }
#  
# return FALSE;
#    
# }

regarde les requêtes de type REPLACE INTO, et encore vu que tu bosses en PHP5, j'imagine que tu bosses avec une bonne version de MySQL, consulte donc (histoire d'optimiser le replace) les requêtes de type "INSERT... ON DUPLICATE KEY UPDATE", tout ton code peut être remplacé par une seule requête.

Je mets 9 parce que l'idée est intéressante :-)

Commentaire de J_G le 30/05/2006 10:36:43

Salut,


Effectivement, tu nous as plus fait un très joli tutorial qu'une source "qui-sauve-la-vie".


!!! MAIS IL MANQUE UN TRUC ESSENTIEL !!!

$_SESSION['tableau'] = array(1,2,3,4,5,6); // BUG
$_SESSION['objet'] = new DOMDocument(); // BUG

Dans ta source, les variable de session sont auvegardées sans concervation du type (Tout est transformé en string). Les tableaux, objets, ressources ne pourront être récupérées

Donc il faut serialize() à l'écriture et unserialize() à la lecture...


Autre chose, pour des questions de performance lors de l'écriture des données. Je te propose de ne pas compter pour savoir si la variable de session est déjà présente... De toute façon tu l'écrasera.
Alors un petit coup de "delete;insert;" en une seule requète fera l'affaire.


PS :
$_SESSION['insertion_SQL'] = '"; drop table session; --'; // Je suppose que tu echape dans ton abstraction SQL

Commentaire de Anthomicro le 30/05/2006 11:14:07

'Autre chose, pour des questions de performance lors de l'écriture des données. Je te propose de ne pas compter pour savoir si la variable de session est déjà présente... De toute façon tu l'écrasera.
Alors un petit coup de "delete;insert;" en une seule requète fera l'affaire.'

C'est ce que je disais juste au dessus :)

Commentaire de FhX le 30/05/2006 12:12:36

"j'aime bien ton script, c'est bien codé, mais qu'est-ce que c'est gourmand :-("
Vi, ca j'avais remarqué et je n'avais pas encore trouvé de solution. Le problème vient bien du SQL, je regarderai donc ca de plus près pour REPLACE INTO.

"Dans ta source, les variable de session sont auvegardées sans concervation du type (Tout est transformé en string)"
Les sessions via fichiers sont aussi sauvegardées en string, sauf que la linéarisation/délinéarisation se fait en automatique habituellement... et apparament pas ici. Je vais remédier au problème :)

"$_SESSION['insertion_SQL'] = '"; drop table session; --'; // Je suppose que tu echape dans ton abstraction SQL" Vi, l'escape_string est réalisé en automatique sur tout ce qui est chaine seulement :) ( méthode ->prepare() )


Bon dès que je fini le boulot ce soir ( pas avant 19h :p ), je m'occuperai de ce que vous avez mis plus haut :)

Commentaire de Anthomicro le 30/05/2006 12:51:14

Bah même en ayant optimisé ça, ça restera largement plus lent qu'un système de sessions avec fichiers. Je trouve ton système intéressant, mais ça aurait pu le faire en implémentant une gestion avec des fichiers en sauvegardant des informations supplémentaires dans le fichier session (je ne sais pas moi, le referer, etc...) en gros initialiser des variables dans la session que tu réutilises ensuite sans les avoir déclarées dans le code PHP. Faudrait trouver une application pratique à ce genre de cas mais je pense qu'un système de stats pourrait en tirer partie couplé à une classe de gestion de logs.

Commentaire de FhX le 30/05/2006 13:06:16

Ah oui, tel quel le système est bien plus lent à cause des requètes. Je comptais justement utiliser ce système pour une gestion de stats derrière un peu plus tard.

Mais ce soir je m'y attèle :)

Commentaire de juki_webmaster le 30/05/2006 13:07:01

Kankrelune > "mais que tu réécris les url avec l'id de session... .. ." > j'ai dut mal m'exprimer, j'utilise l'id de sessions maison , j'utilise pas les $_SESSION, justement.


"en gros tu veux limiter le nombre de visiteurs à ton site... et il fait quoi le 11 eme internaute qui veut se connecter à ton site. " > 10 sessions / IP simultané (ip+hots), pour eviter que la db ne souffre par flood (ex: je m'identifit, je me reidentifit, 1000 fois de suite etc...), donc generer autemps de sessions inutiles.
IP+Host, pour la simple et bonne raison: une ip peut etre reatribuer, mais l'host lui theoriquement ne sera pas le meme, exemple: une IP Wanadoo, l'ip je la rajoute pour avoir quelque chose en cas où l'host n'est plus valide: une IP Wanadoo.
10 sessions/Ip : Imagine un sous reseau avec 3 pc une meme IP publique connecté à ton site, si ils perdent leurs id de session, ils vont devoir la reecrer, mais il leurs restera encore quelques essaies. Tout cela pour eviter de mettre en danger MySQL.

Et à ce que je sache, ont a pas tous la meme IP..


Et si tu ne comprend pas pourqoui il faut effacer toutes les sessions expirer (je parle des sessions coté db), c'est tout simple: tout le monde ne clique pas sur "Deconnexion".

Kankrelune, as-tu une autre <quote></quote> pour la route?

Commentaire de kankrelune le 30/05/2006 13:51:37

[quote]"en gros tu veux limiter le nombre de visiteurs à ton site... et il fait quoi le 11 eme internaute qui veut se connecter à ton site. " > 10 sessions / IP simultané (ip+hots), pour eviter que la db ne souffre par flood (ex: je m'identifit, je me reidentifit, 1000 fois de suite etc...), donc generer autemps de sessions inutiles.
IP+Host, pour la simple et bonne raison: une ip peut etre reatribuer, mais l'host lui theoriquement ne sera pas le meme, exemple: une IP Wanadoo, l'ip je la rajoute pour avoir quelque chose en cas où l'host n'est plus valide: une IP Wanadoo.
10 sessions/Ip : Imagine un sous reseau avec 3 pc une meme IP publique connecté à ton site, si ils perdent leurs id de session, ils vont devoir la reecrer, mais il leurs restera encore quelques essaies. Tout cela pour eviter de mettre en danger MySQL.
[/quote]

Oui parce que toi tu te base sur l'ip... mais dans le cas d'une session classique comme c'est le cas ici l'ip on s'en fout... .. .

[quote]Et si tu ne comprend pas pourqoui il faut effacer toutes les sessions expirer (je parle des sessions coté db), c'est tout simple: tout le monde ne clique pas sur "Deconnexion".[/quote]

C'est toi qui ne doit pas avoir compris ce que je voulais dire... tu crois qu'entre le moment ou je tape ce message et le moment ou je vais le soumettre il y en aura beaucoups des sessions expirées... alors imagine sur un site à petit traffic... faire un gc à chaque instanciation de la class ça veut dire une requete de plus à chaque chargement de page (voir plus si tu fais pas de l'instanciation unique) c'est totalement inutile... si tu regarde le systeme de session par défaut le garbage collection se déclenche en moyenne toute les 100 éxécutions (1% de probabilité de déclenchement)... c'est quoi le mieux... déclencher le gc à chaque instanciation pour virer 3 pauvres sessions expirées ou le déclencher toute les 30 minutes pour en virer 50 en un coups... .. .

et hop...

[quote][/quote]

Pour la forme... .. . ;o)

^_^

@ tchaOo°

 Ajouter un commentaire


Discussions en rapport avec ce code source dans le forum

problème avec les sessions [ par lpefec ] mon script ne fonctionne pasidentification.php----------------------------------------------------------------&lt;html&gt;&lt;head&gt;&lt;title&gt;Log problème avec les sessions [ par lpefec ] mon script ne fonctionne pasidentification.php----------------------------------------------------------------&lt;html&gt;&lt;head&gt;&lt;title&gt;Log $_session et IE (cookies de session) !! [ par rocknroll2 ] J'ai un site avec PHP et SQL, Mes $_session ne fonctionnent que si l'utilisateur coche "accepter les cookies de session" dans sa configuration d'IE ! Erreur de session [ par Tilix ] Salut, Pour test&#233; mon mini syst&#232;me d'authentification, j'veux affich&#233; l'ID authentifi&#233; sur la page d'accueil J'ai donc : AUTH Problème de redirection [ par jipe32 ] Bonsoir, j'ai un code qui redirige vers une autre page si la quantité demandé dans le formulaire précédent dépasse la quatité disponnible (je fais un Garder les variables de sessions [ par Anthotill ] Bonjour à tous;J'ai un probleme pour garder lors d'un parcours sur mon site les variables de session.Dans une BDD j'ai un champ userid en autoincremen Probleme session [ par Anthotill ] Bonjour à tous J'ai un probleme avec les sessions dans mes pages.Je précise que ma base de donnée se trouve sur un https:// ...A chaque début de page, pbroblème de session [ par Miss1 ] Salut,je veux crée une session pour que l'utilisateur puisse accédé a son compte panier.Le probleme qui intervien' c'est que quand je rentre  acun log Liste de membres [ par biloutte33 ] Bonjour tout le monde,suite à mon espace membre qui est actif, j'aimerais que l'on puisse consulter les fiches des membres avec leurs informations res Projet PHP5/SQL [ par alexgeek ] Bonjour,Je suis débutante en PHP et j'ai un projet en php my sql à faire puisque je suis en stage, si quelqu'un pouais m'aider à le faire je lui serai


Nos sponsors


Sondage...

CalendriCode

Mars 2010
LMMJVSD
1234567
891011121314
15161718192021
22232425262728
293031    

Consulter la suite du CalendriCode

 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 0,593 sec (4)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales