DELPHPDate de publication : 24/09/06 
        Par
        Sub0 
     Ce tutoriel est destiné à vous montrer comment j'utilise un composant 
     d'accès Http pour exécuter des scripts PHP avec Delphi et accéder à une 
     base de données MySQL.  En exemple, la réalisation d'un espace membre PHP.
    
       1. PRESENTATION 1-1. INTRODUCTION 1-2. «DELPHP» 1-3. CONVERSIONS DES DONNEES AVEC DELPHI 1-4. DELIMITATION DES DONNEES PHP 1-5. TIMEOUT & PROGRESSION AVEC DELPHI 1-6. SOURCES 2. ESPACE MEMBRE DELPHP 2-1. UTILISATION 2-2. INSTALLATION & CONFIGURATION 2-2-1. SOURCES 2-2-2. INSTALLATION PHP 2-2-3. CONFIGURATION PHP 2-2-4. SESSIONS PHP 2-2-5. DROITS D'ACCES 2-2-6. TABLE DE DONNEES 2-2-7. INSTALLATION DELPHI 2-2-8. CONFIGURATION DELPHI 3. ADMINISTRATION 3-1. LISTER LA BASE 3-2. STRUCTURE DE LA TABLE 3-3. DONNEES DE LA TABLE 3-4. ACCEDER A UN ENREGISTREMENT 3-5. SOURCES 3-6. REMERCIEMENTS 1. PRESENTATION1-1. INTRODUCTION
              Apparemment, un grand nombre d'hébergeurs web ne permettent plus 
              l'accès direct aux bases de données avec des composants SGBD; 
              La solution que j'ai adopté est d'utiliser des scripts PHP, 
              hébergés sur le même serveur que la base de données et d'exécuter 
              ces scripts avec un programme Delphi.
             
              Ce tutoriel est destiné à vous montrer comment j'utilise un 
              composant d'accès Http pour exécuter des scripts PHP avec Delphi 
              et pouvoir accéder aux bases de données MySQL. Ce tutoriel 
              contient également le code source d'un espace membre très complet 
              et une petite démo d'administration des données MySQL, ainsi que 
              toutes les informations nécessaires pour installer les scripts et 
              configurer ces programmes.
             
              Cet article est aussi disponible...  - Au format HTML : http://sub0.developpez.com/delphp/index.php - Au format PDF : http://sub0.developpez.com/tutoriel/delphp.pdf - Sur mon serveur FTP : ftp://ftp-developpez.com/sub0/tutoriel/delphp.pdf 1-2. «DELPHP»
              J'ai baptisé ce projet DELPHP. Son principal avantage par rapport 
              aux composants SGBD est qu'il n'est pas nécessaire de fournir le 
              login d'accès de la base de données dans le programme Delphi. Il 
              n'est pas nécessaire non plus d'installer de nouveau composant 
              dans la palette de Delphi. Toutes les sources sont disponibles 
              gratuitement. Le principe est le même qu'un navigateur web. 
             
              Pour exécuter les scripts PHP, j'utilise le composant 
              THttpCli d'ICS. J'ai récupéré le code de ce composant 
              (3 unités) afin de pouvoir l'utiliser dynamiquement dans mes 
              projets. Ainsi, cela vous évitera l'installation de toute la 
              palette d'ICS juste pour compiler les démos.   
             
              Pour simplifier son utilisation, j'ai réalisé une Class nommée 
              THttpPost dans l'unité DelphpUnit.pas. Cette Class 
              permet de communiquer avec les scripts PHP. Le code suivant vous 
              montre comment je l'utilise pour simuler le Post d'un formulaire, 
              plus précisément, poster 3 champs de saisie texte vers le script 
              "connect.php" de mon serveur local : 
 
              1) Post du formulaire avec DELPHI : La méthode ResetPost sert à effacer la liste des champs de saisie. La méthode AddPost, à ajouter chaque champs de saisie. Note : Vraisemblablement, la taille d'une requête Http est limitée à 8Mo maximum. 
              2) Réception du formulaire avec PHP : 
La réception des champs de saisie postés avec Delphi se fait de façon tout à fait normale avec le tableau $_POST. Sur le serveur, le script connect.php réceptionne les données comme ceci : 
 
              Il est tout de même conseillé de vérifier la syntaxe des données 
              avant de les utiliser pour contrer un éventuel piratage par 
              injections SQL. Pour cela, vous pouvez utiliser la fonction 
              Verifie_Syntax contenue dans le script func.php. Par 
              exemple, le code PHP suivant vérifie que le pseudo ne contient que 
              des chiffres ou des lettres en minuscule :
             
 3) Réception des messages PHP avec DELPHI : Pour différencier les messages d'erreurs et les messages de données, un caractère spécial (149) suivi d'un espace (32) est placé au début des données. Ainsi, lorsque le programme Delphi réceptionne le résultat du script PHP, il teste les 2 premiers caractères pour déterminer si il s'agit d'un message d'erreur ou non : - Les messages d'erreur seront placés dans la chaîne StringError. - Les messages de données, dans la chaîne StringResult. 1-3. CONVERSIONS DES DONNEES AVEC DELPHI
              Etant donné que les messages d'erreur de PHP sont au format HTML, 
              j'ai réalisé une petite fonction Delphi nommée StripHtmlTags 
              pour supprimer les balises HTML et pouvoir afficher proprement les 
              messages avec la fonction ShowMessage de Delphi. Cette fonction 
              remplace également les balises <BR> par des sauts de ligne 
              Windows (#13). Cela dit, dans le cas où vous souhaiteriez envoyer 
              du code HTML vers votre programme Delphi, utilisez le mode binaire 
              de la méthode StartPost pour conserver le format initial 
              des données.  
             
              Vous disposez également de la fonction Delphi StrExplode 
              pour découper une chaîne de caractère en liste de chaîne. Cette 
              fonction est prévue pour convertir le résultat PHP en tableau de 
              chaîne et ainsi récupérer des tableaux PHP dans le programme 
              Delphi.  
             
              La fonction Delphi AddSlashes permet d'échapper les 
              caractères spéciaux avant d'effectuer des requêtes SQL avec 
              Delphi. Cette fonction est nécessaire si vous souhaitez 
              enregistrer ou modifier du texte dans votre base de données. 
              Noter que cette fonction pourrait aussi être effectuée avec PHP 
              lors de la réception des données.   
             1-4. DELIMITATION DES DONNEES PHP
              Durant les premiers tests du code, [Silk], un membre du forum, a 
              eu l'occasion de l'installer sur un hébergement gratuit où une 
              bannière de publicité est automatiquement ajoutée sur toutes les 
              pages de l'hébergement. A cause de cela, les données réceptionnées 
              avec Delphi étaient altérées car le programme réceptionnait aussi 
              le code HTML de la bannière sans pouvoir faire de distinction. 
             
              Pour résoudre ce problème, il a fallu encadrer les données émises 
              par les scripts PHP avec 2 chaînes de caractères spéciaux. La 
              fonction Mydie du script func.php s'occupe justement 
              d'ajouter ces délimiteurs au début et à la fin de tous les 
              messages PHP. De cette manière, lors de la réception des messages, 
              Delphi supprime les données situées à l'extérieur des délimiteurs.
             1-5. TIMEOUT & PROGRESSION AVEC DELPHI
              Le composant THttpPost possède un TimeOut qui est réinitialisé 
              chaque fois que le programme réceptionne des nouvelles données. 
              Cela est différent du temps total d'exécution. Il s'agit plutôt 
              d'un TimeOut d'inactivité. 
             
              La propriété MaxTimeOut définit la valeur de ce TimeOut en 
              millisecondes. Par défaut, sa valeur est fixée à 10000, soit 10 
              secondes. Selon le programme, il est probable qu'il faille 
              augmenter ce délai pour des requêtes très longues à répondre. Il 
              est conseillé d'utiliser la fonction flush de PHP pour 
              demander au script d'émettre les données lorsque cela est possible.
             1-6. SOURCES
              Télécharger le code de l'exemple ci-dessus : delphpunit.zip 1) Installer le script "connect.php" sur votre serveur 2) Définir l'url de ce script dans la propriété URL de l'objet HttpPost1 
              Vous pouvez choisir d'utiliser la dernière version du composant 
              d'ICS : HttpCli6.zip L'unité se nomme OverbyteIcsHttpProt au lieu de HttpProt... 2. ESPACE MEMBRE DELPHP
            Ce programme est destiné à être utiliser dans un projet tel qu'un 
            tchat ou un jeu en ligne. Il s'intègre très facilement et s'utilise 
            comme une boîte de dialogue modale. Il intègre un formulaire 
            d'inscription, un formulaire d'identification et de réinscription 
            (perte de mot de passe). Cet espace membre est configurable pour 
            obtenir un niveau plus ou moins sécurisé. Le fonctionnement est le 
            même qu'un espace membre en PHP excepté que les formulaires sont 
            programmés et affichés avec Delphi au lieu d'être affichés par un 
            navigateur web au format HTML. 
           2-1. UTILISATION
 
 
 
 2-2. INSTALLATION & CONFIGURATION2-2-1. SOURCES
                L'archive contient les fichiers sources pour Delphi ainsi que les 
                scripts PHP. Ces-derniers sont regroupés dans une archive nommée "php.zip" : loginunit.zip 2-2-2. INSTALLATION PHP
                Tous mes projets PHP/MySQL débutent toujours avec les 3 mêmes scripts : - config.php contient les constantes, tableaux et les variables globales du projet. - bdd.php contient les fonctions de création et d'accès de la base de données. - func.php contient diverses fonctions. 
                Les scripts suivants sont spécifiques à l'espace membre : - connect.php contient le code qui vérifiera la connection du client. - admin.php est la page sécurisée du programme. - font.php est en fait la police "Arial.ttf" utilisée pour les images de code. 
                Ces scripts sont à installer sur votre serveur, dans le dossier 
                de votre choix (par défaut, utilisez le nom de dossier "delphp"). 
               2-2-3. CONFIGURATION PHP
                Avant toutes choses, vous devez définir les paramètres de 
                configuration du programme qui se trouvent dans le script 
                config.php, en particulier les paramètres d'accès de la 
                base de données.
               
                Ci-dessous, le détail des autres paramètres de configuration :
               
                $cfg['mail_adm']='delphp@server.fr'; Adresse mail de l'administrateur. Cette adresse est utilisée pour définir l'expéditeur dans les mails d'activation. Elle est aussi utilisée comme destinataire pour envoyer un mail d'alerte lors d'une éventuelle attaque. Si ce paramètre est vide, aucun mail d'alerte est envoyé à l'administrateur. 
                $cfg['mail_act']=true; Activation de l'inscription par mail. Ce paramètre doit être activé si vous souhaitez sécuriser votre espace membre et obtenir une adresse mail valide pour chaque membre. Si ce paramètre est mis à False, les inscriptions seront automatiquement activées dès leur création. 
                $cfg['delay_act']=2*3600; Durée de réservation du pseudo (par défaut, 2 heures). Ce paramètre n'est valable qu'avec une activation par mail. Il permet de libérer le pseudo dans le cas où l'inscription n'est pas activée dans ce lapse de temps. 
                $cfg['perim_act']=true; Détermine si le mail d'activation devient inutilisable dans le cas où le lien ne serait pas exécuté dans le lapse de temps défini par la paramètre précédent "delay_act". Si ce paramètre est mis à False, la libération du pseudo est effectuée à la fin du temps imparti mais l'activation du compte reste malgré tout possible seulement si le pseudo n'est pas réutilisé par une autre inscription. 
                $cfg['im_protect']=true; Les inscriptions sont protégées par une image de code anti-robots. Ces images sont au format Jpeg. Elles sont enregistrées dans le dossier temporaire défini par le paramètre "tmp_dir". Lors de la toute première création d'une image de code, le dossier est créé si il n'existe pas et un fichier nommé "index.php" est ajouté dans ce dossier afin d'interdire l'affichage de son contenu et ainsi, protéger le nom de fichier des images. Le nom d'une image de code est obtenu avec le chiffrage md5 du code qu'elle contient, précédé d'un second code aléatoire (imastr). Ce-dernier est une empreinte du code de l'image et permet de reconnaître l'image sans devoir fournir le code qu'elle contient. Ces images sont automatiquement supprimées du dossier lorsque leur durée de vie est supérieure ou égale à la durée de vie d'une session, soit 5 minutes. L'image est également supprimée du dossier une fois qu'elle a été utilisée. 
                $cfg['max_att']=4; Nombre maximal d'échecs ou d'attaques consécutifs admissibles. Ce paramètre est utilisé pour bloquer le compte ou annuler l'inscription lorsque l'utilisateur fournit une suite de mots de passe ou clés d'activation invalides. Lors d'une suppression d'inscription ou de réinscription, un mail d'alerte contenant toutes les informations possibles sur l'attaque est envoyé à l'administrateur. Si ce paramètre est égal à zéro, le compte n'est pas bloqué et le nombre d'échec est illimité. Néanmoins, un délai de 3 secondes est ajouté à chaque essai afin de freiner le robot en cas de tentative de piratage du mot de passe par force brute. Concernant l'activation du compte si ce paramètre est égal à zéro, l'utilisateur ne disposera que d'un seul essai. 
                $cfg['delay_att']=15*60; Durée de blocage du compte (par défaut, 15 minutes). Lorsque le temps de blocage est dépassé, l'utilisateur dispose d'un nouvel essai. 
                $cfg['max_newmdp']=3; Nombre maximal de réinscriptions admissibles utilisé pour pouvoir effectuer une demande de nouveau mot de passe. Si ce paramètre est égal à zéro, le membre ne pourra pas changer de mot de passe. Dans ce cas, le formulaire de perte de mot de passe est désactivé. 
                $cfg['max_compte']=3; Nombre maximal de compte pour un poste. Le programme utilise l'IP et/ou l'adresse mail pour déterminer si il s'agit d'un même poste ou non. Si ce paramètre est égal à zéro, les inscriptions ne seront plus permises. 
                $ses['ses_name']='delphp'; Nom utilisé pour la session. Ce paramètre est également utilisé pour nommer l'expéditeur dans le mail d'activation. 
                $ses['lifetime']=5*60; Durée de vie de la session (par défaut, 5 minutes). Ce paramètre est aussi utilisé pour la durée de vie des images de code. Ainsi, toutes les images de code qui ont plus de 5 minutes sont automatiquement supprimées du dossier temporaire. Cela signifie aussi que l'utilisateur ne disposera que de 5 minutes pour s'inscrire. Dans le cas contraire, une nouvelle image de code lui sera fournie. 
                SMTP : 
                Pour pouvoir sécuriser correctement cet espace membre, vous devez 
                définir le paramètre SMTP de votre serveur de façon à ce qu'il 
                puisse envoyer des mails. Ce paramètre se trouve dans le fichier 
                php.ini. De la même manière, vous devez activer 
                l'extension GD2 pour pouvoir utiliser les images de code avec la 
                police Arial. N'oubliez pas qu'il est nécessaire de redémarrer 
                votre serveur pour que ces changements soient pris en compte. 
                Si vous souhaitez utiliser un serveur SMTP distant, je vous 
                conseille l'utilisation de la librairie PHPMailer.
               2-2-4. SESSIONS PHP
                Les sessions permettent de mémoriser l'identification. Une session 
                sera valable 5 minutes, durée définie dans le script config.php. 
                Il y a en tout 4 fonctions dédiées aux sessions : Création, test 
                de connection, destruction, regénération du SID. Elles sont 
                déclarées dans le script func.php. 
               
                Le composant THttpCli d'ICS utilise l'événement OnCookie 
                pour récupérer le SID et le retransmettre automatiquement au 
                serveur. Le SID est redéfini à chaque connection par le script 
                PHP pour contrer le vol de session. Le SID intègre le pseudo et 
                un code aléatoire unique pour ne permettre qu'une seule 
                connection possible par pseudo.
               
                Petite parenthèse, surtout si vous débutez avec PHP, sachez que : 1) Les fonctions de sessions fonctionnent comme pour les headers : Il n'est pas possible d'utiliser les fonctions session_start ou header si le script PHP a déjà affiché quelque chose. Dans ce cas, vous aurez un message d'erreur du genre : Warning: Cannot modify header information - headers already sent by (output started at... 2) A propos des messages d'erreur PHP justement, je vous rappelle qu'un arobas @ qui précède une fonction permettra de masquer les messages d'erreur PHP de cette fonction. Ne l'utilisez que si vous êtes sûr de votre code ou que si vous gérez vous-même l'affichage des erreurs. 2-2-5. DROITS D'ACCES
                Pour protéger n'importe quel script PHP, il suffira de placer au 
                tout début du script, le code suivant :
               
 
                Ainsi, seuls les membres authentifiés auront la permission 
                d'utiliser ce script.
               
                Il serait facile d'attribuer des droits pour chacun des membres 
                en ajoutant un champs supplémentaire dans la table de données et 
                un test dans le code PHP... Une autre possibilité plus simple 
                est d'utiliser directement le pseudo. Par exemple, vous devez 
                vous inscrire avec le pseudo d'accès de votre base de données 
                (root) pour vous réserver les droits d'administration et ainsi 
                interdire l'accès d'une fonction si le membre ne s'est pas 
                identifié comme l'administrateur :
               
 2-2-6. TABLE DE DONNEES
                Voici une rapide description du contenu de la table «membres». 
                Cette table est créée automatiquement, comme la base de données 
                d'ailleurs, si elles n'existent pas. A noter que la commande 
                "IF NOT EXISTS" n'est disponible qu'à partir de la version 4 de 
                MySQL. Voici la structure de cette table :
               
 
                Le champs newmdp (n°4) est utilisé lors d'une demande d'un 
                nouveau mot de passe. L'ancien mot de passe reste 
                valide tant que le nouveau mot de passe n'est pas complètement 
                activé. La clé contenue dans le champs newmdp intègre 4 éléments 
                séparés par le caractère "|". Le mail d'activation fournit juste 
                le premier élément de cette clé. Ces éléments sont dans l'ordre : 
                La clé d'activation composée d'un code aléatoire et du pseudo 
                codé en hexadécimal (ASCII), le nouveau mot de passe chiffré 
                avec md5, la date de la demande du nouveau mot de passe utilisée 
                pour calculer la durée de vie de cette demande, et enfin, du 
                nombre d'échec d'activation du nouveau mot de passe.
               
                Le champs compte (n°5) contient le nombre de compte activé 
                pour un poste. L'IP et l'adresse mail sont utilisées pour 
                identifier le poste.
               
                Le champs ip (n°10) contient l'IP du membre suivi d'un 
                séparateur "|" et de l'Host.
               Lors d'une inscription activée par mail, la clé 
                d'activation est enregistrée dans le champs active (n°11). 
                Cette clé est composée d'un code aléatoire et du pseudo codé en 
                hexadécimal (ASCII). Le mail d'activation fournit juste cette 
                première partie de la clé. La clé détient également le nombre 
                d'échec d'activation séparé par le caractère "|". Le champs 
                "active" est mis à 0 lorsque le compte est bloqué et à 1 
                lorsque le compte est activé.
               
                Le champs attack (n°12) est dédié à l'enregistrement 
                d'échecs d'identification et fournit le nombre d'essai encore 
                disponible. 
               2-2-7. INSTALLATION DELPHI
                L'espace membre s'intègre facilement dans un projet. Il suffit 
                d'ajouter l'unité LoginUnit.pas à votre fiche principale de votre 
                projet dans le gestionnaire de projet. Ainsi, l'espace membre se 
                comportera comme une boîte de dialogue modale. Dans le fichier 
                DPR, vous obtenez l'ordre de création suivant :
               
 
                De cette manière, la fiche de l'espace membre est créée juste 
                après et détruite juste avant la fiche principale du projet. 
                L'ouverture du dialogue peut se faire soit manuellement en 
                exécutant la méthode ShowFormulaire ou TestConnection, soit 
                automatiquement à la création de la fiche ou lorsque le programme 
                accède à un script PHP sécurisé et que l'utilisateur n'est pas 
                encore identifié.
               2-2-8. CONFIGURATION DELPHI
                Le principal paramètre du programme est l'url du serveur utilisé 
                pour accéder aux scripts PHP. Cette adresse se trouve dans le 
                code source du programme, mais il est également possible de 
                définir cette url dans le fichier INI du programme. De la même 
                manière, vous pouvez redéfinir le port et la valeur du TimeOut. 
                Il conviendra d'adapter le programme à votre application. En 
                général, vous ne donnerez pas la possibilité aux utilisateurs de 
                modifier ces paramètres.
               
                D'autres paramètres sont disponibles dans l'unité LoginUnit.pas :
               
 
                Il est prévu également de pouvoir choisir la couleur du dialogue. 
                Par défaut, ces couleurs sont celles du système (clDefault). 
                L'image de code utilise également les couleurs du dialogue. 
                Voici 3 exemples de couleurs personnalisées :
               
 3. ADMINISTRATION
            Cette dernière partie de l'article montrera comment j'utilise la 
            connection aux scripts PHP pour effectuer des requêtes SQL précises 
            et récupérer le résultat avec Delphi. En particulier, lister les 
            tables de la base de données, afficher la structure et les données 
            d'une table et supprimer un ou plusieurs enregistrements dans une 
            table. Ces commandes sont contenues dans le script admin.php et 
            protégées par l'espace membre Delphp. Vous devez donc d'abord vous 
            identifier avant de pouvoir les utiliser.
            
            Il serait tout à fait possible de poster la requête SQL complète pour 
            permettre au programme Delphi de pouvoir effectuer n'importe quelle 
            requête, mais je préfère définir les requêtes avec PHP, le programme 
            Delphi fournissant juste les paramètres de ces requêtes. Je trouve 
            cette méthode plus sécurisante. 
           
            Il serait également possible d'utiliser le format XML ou CSV ou un 
            autre format perso pour récupérer et afficher les données des tables... 
            Bref, il appartient au développeur de choisir la manière dont il 
            communiquera avec la base de données. Pour ma part, côté PHP, je 
            sérialise les données séparées par le caractère nul #0, puis côté 
            Delphi, j'utilise la fonction StrExplode pour obtenir le tableau de 
            données (TStrings).
           ![]() 3-1. LISTER LA BASE
              Le programme Delphi poste la commande "act=getbase" avec la 
              méthode StartPost. Le résultat de cette requête est la liste 
              des tables de la base sérialisée dans la variable HttpPost1.StringResult. 
              La fonction StrExplode scinde cette chaîne pour obtenir la 
              liste déroulante finale des tables :
             
 3-2. STRUCTURE DE LA TABLE
              Avec la commande "act=gettable" et le nom de la table en 
              paramètre, le script PHP retourne la structure de la table (le nom 
              et le type de chaque colonne). Comme pour obtenir la liste des 
              tables, StrExplode scinde le résultat dans une variable de 
              type TStrings nommée "structure" :
             
 3-3. DONNEES DE LA TABLE
              De la même manière, avec la commande "act=getdata", le 
              programme récupère les données et les stocke dans la variable de 
              type TStrings nommée "Donnees". La structure de la table 
              récupérée auparavant permettra de démultiplexer ces données. Dans 
              cette commande sont prévus en tout 5 paramètres pour définir la 
              requête SELECT. Les paramètres porder et pdesc 
              permettront d'effectuer le tri des données par colonne.
             
 3-4. ACCEDER A UN ENREGISTREMENT
              Ce code est destiné à vous montrer comment j'utilise les données 
              téléchargées pour reconstituer le paramètre WHERE d'un 
              enregistrement pour y accéder avec une requête SELECT, DELETE ou 
              UPDATE... Vous remarquerez l'utilisation de la fonction 
              AddSlashes pour échapper les caractères spéciaux qui 
              pourraient se trouver dans les données. Dans le code suivant, 
              Y représente le numéro de la ligne de la grille où se 
              trouve l'enregistrement :
             
 3-5. SOURCES
              Télécharger le code du programme d'administration : 
              admindemo.zip 1) Installer les scripts php sur votre serveur 2) Définir l'url du script "admin.php" dans la propriété URL de l'objet HttpPost1 3-6. REMERCIEMENTS
              Si vous avez des remarques et suggestions intéressantes pour 
              améliorer le code ou des questions concernant cet article, je suis 
              à votre disposition sur le forum. N'oubliez pas que vous disposez 
              de la FAQ et du forum PHP de Developpez.com pour toutes questions 
              concernant la programmation dans ce language.
             
              Merci à François Piette et les membres du forum web et Delphi de 
              Developpez.com pour leur participation (pardon si j'en oublie) : 
              Archibal29, chaours, ero-sennin, giloutho, Giovanny Temgoua, 
              Laurent Dardenne, mussara, pedro, rezuss, [Silk], sjrd, Yogui 
              et wilco. 
             à+   
            
     Ce document est issu de http://www.developpez.com et reste la propriété 
     exclusive de son auteur. La copie, modification et/ou distribution par 
     quelque moyen que ce soit est soumise à l'obtention préalable de 
     l'autorisation de l'auteur.
    
	   |