Trieur Organisé de Solutions et de Ressources Informatiques

logo du site TOSRI

Développement › PHP

Article(s) du 18 février 2013

Niveau : personne ayant des bases en informatique (terminologie, principe)

Stop au spam Referer

Pour éviter d'avoir des stats faussées dans l'Awstat par des bots visitant le site avec de faux en-tête referer :

Dans le .htaccess :

SetEnvIfNoCase Referer casino spammer=yes

Order allow,deny
Allow from all
Deny from env=spammer

On peut tester avec le petit plugin firefox HeaderCOntrolRevived. Et voir dans les logs d'erreur apache :

[Mon Feb 18 12:19:08 2013] [error] [client xxxxxxxxxx] client denied by server configuration: /xxxx/www.librement-votre.fr/xxxx/, referer: http://casino.fr

http://codex.wordpress.org/Combating_Comment_Spam/Denying_Access#Deny_Access_Referrer_Spammers


Article(s) du 4 avril 2012

Niveau : personne ayant des bases en informatique (terminologie, principe)

Vérifier les messages de PDO

try{
    $dbHandle = new PDO('sqlite:xxxxx.sqlite3');
    $dbHandle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch( PDOException $exception )
{
    die($exception->getMessage());
}

http://www.php.net/manual/fr/pdo.error-handling.php

Article(s) du 22 février 2012

Niveau : personne ayant des connaissances informatique de niveau BAC+2/BAC+3

Uploader vos fichiers via PHP en 3 lignes

Introduction

Bonjour,

Non je ne vous mens pas vous pouvez envoyer vos fichiers sur un serveur par un formulaire HTML en seulement 3 lignes de codes, démonstration :

<?php
    include_once "Uploader.php";
    $up = new Uploader();
    $r = $up->upload("monFichier", $dossier);
?>

Facile hein ?

Bon d'accord, je vous explique : j'utilise un fichier qui contient une classe PHP que j'ai réalisée qui effectue des uploads via un minimum de paramètres. Je ne détaillerais pas totalement cette classe au sein de cet article, le fichier est commenté. Je vais donc simplement faire une démonstration des méthodes qu'elle utilise.
 

Présentation externe

Commençons par mettre ses 3 lignes au milieu d'un code plus complet et surtout plus détaillé/commenté :

<?php
// définir un dossier de sauvegarde des fichiers envoyés
define("dossier", dirname(__FILE__)."/fichiers");

// inclure la classe de l'Uploader
include_once "Uploader.php";

    // création d'un objet Uploader
    $up = new Uploader();

    // test pour savoir si des fichiers ont été envoyés
    if($up->demande()){
        $r1 = $up->upload("monFichier", dossier);             // Upload du fichier "monFichier"       
        $r2 = $up->uploads("monFichier2", dossier, false);    // Upload des fichiers "monFichier2"
    }

// un formulaire qui fait appel à la même page à la validation et qui peut envoyer des fichiers
?>
<form action="?" method="post" <?php echo $up->attForm()?>>
<?php
echo $up->inputSize(); // écriture d'un champ pour limiter la taille des fichiers à envoyer

// puis les champs pour envoyer les fichiers et un bouton de validation
?>
    <input type="file" name="monFichier" size="50" /><br />
    <input type="file" name="monFichier2[0]" size="50" /><br />
    <input type="file" name="monFichier2[1][a]" size="50"/><br />
    <input type="file" name="monFichier2[1][b]" size="50"/><br />
    <input type="submit" value="GO" />
</form>

Vous obtenez visuellement :






Pour ceux qui n'ont pas encore cliqué sur les boutons, vous ne pouvez pas envoyer de fichiers. ^^

Ensuite, les lignes qui ne sont pas claires :

if($up->demande()){

C'est tout bêtement une fonction qui regarde si $_FILES contient des éléments, son code est simple :

function demande(){
    return !empty($_FILES);
}


Les méthodes "upload()" et "uploads()"  seront vues plus loin, voyons d'abord la page sans envoyer de fichiers.

<form action="?" method="post" <?php echo $up->attForm()?>>

L'entête du formulaire est étrange n'est-ce pas ?
En fait un formulaire qui envoie des fichiers doit posséder un attribut particulier : enctype="multipart/form-data"
Comme on dit que les développeurs sont feignants, cette fonction vous permet de ne pas connaitre la syntaxe exacte de l'attribut, affichez juste le résultat de cette méthode.

function attForm(){
    return 'enctype="multipart/form-data"';
}

Ensuite cette ligne au début du formulaire :

echo $up->inputSize();

Elle n'est pas obligatoire, elle affiche un champs de type input hidden qui limite la taille des fichiers à envoyer. Elle peut prendre un paramètre qui correspond en octets à la taille maximale des fichiers.

Les 4 input file qui suivent correspondent aux différentes syntaxes de l'attribut "name" pour envoyer des fichiers.
 

Présentation des méthodes principales

Les méthodes "upload()" et "uploads()". Que font-elles ?

Ces méthodes permettent de déplacer les fichiers depuis la zone temporaire du serveur vers le dossier de votre choix.

La première prend entre 2 et 4 paramètres et la seconde entre 2 et 3. Leur différence hormis le nombre de paramètres est que l'une envoie un seul fichier alors que la seconde peut envoyer plusieurs.

Définition de la fonction "upload"

function upload($name, $dossier, $ecraser=false, $indices=array())

Détails des paramètres :

  1. Une chaine de caractères correspondant à l'attribut name d'un input file.
  2. Une chaine de caractères correspondant au dossier dans lequel doit être transféré le fichier.
  3. Un booléen pour déterminer si en cas de fichier possédant le même nom on écrase l'existant ou pas.
  4. Un tableau d'indices sous forme de chaine de caractères. Ce paramètre sera vu plutôt avec la seconde méthode, considérons le comme un tableau vide.

Avec les paramètres détaillés je me demande si il est nécessaire d'expliquer ce que fait cette méthode : elle déplace le fichier identifié par "$name" dans le dossier "$dossier" en écrasant ou pas le fichier si il existe déjà avec "$ecraser".

Avant de décrire la seconde fonction je vais préciser un point qui doit être flou depuis un certain temps maintenant : "Pourquoi avoir créé 2 fonctions pour la même opération ?".

Tout simplement parce que la structure de $_FILES dépend de la forme des attributs "name" des "input", je veux parler de la structure en tableau. Affichons ce que $_FILES retourne avec le formulaire ci-dessus :

Array
(
    [monFichier] => Array
        (
            [name] =>
            [type] =>
            [tmp_name] =>
            [error] => 4
            [size] => 0
        )

    [monFichier2] => Array
        (
            [name] => Array
                (
                    [0] =>
                    [1] => Array
                        (
                            [a] =>
                            [b] =>
                        )

                )

            [type] => Array
                (
                    [0] =>
                    [1] => Array
                        (
                            [a] =>
                            [b] =>
                        )

                )

            [tmp_name] => Array
                (
                    [0] =>
                    [1] => Array
                        (
                            [a] =>
                            [b] =>
                        )

                )

            [error] => Array
                (
                    [0] => 4
                    [1] => Array
                        (
                            [a] => 4
                            [b] => 4
                        )

                )

            [size] => Array
                (
                    [0] => 0
                    [1] => Array
                        (
                            [a] => 0
                            [b] => 0
                        )

                )

        )

)

Dans le cas de "monFichier2" on voit que les résultats sont également sous la forme d'un tableau. Comme il est possible de souhaiter un comportement différent pour chaque fichier avec le même name, j'ai ajouté cette seconde méthode.

Définition de la fonction "uploads"

function uploads($name, $dossiers, $ecraser=false)

Détails des paramètres :

  1. Une chaine de caractères correspondant à l'attribut name d'un input file.
  2. Un tableau de chaines de caractères ou une chaine de caractères correspondant aux dossiers dans lesquels doivent être transférés les fichiers envoyés.
  3. Un tableau de booléens pour déterminer si en cas de fichier possédant le même nom on écrase l'existant ou pas.

Vous remarquez que là on ne voit plus le paramètre 4 de la méthode précédente.
En fait ce paramètre de "upload" a été mis car il est utilisé par "uploads". Il s'agit de la liste des indices qui permet de déterminer quel est l'exact fichier parmi ceux de "monFichier2" on souhaite traiter (exemple : pour pour monFichier2[1][b], il faut passer le tableau : array("1", "b")).

Dans le cas de plusieurs fichiers la recherche des indices est interne, puis on fait appel à "uplload()", d'où l'utilité de son 4ème paramètre.

Vous voyez que le second paramètre (les dossiers de destination) a changé depuis la première méthode. En effet vous pouvez utiliser un tableau de dossier pour définir un dossier de destination par fichier. Pour cela les indices doivent correspondre à ceux du "name".
Exemple :

Array
(
    [0] => dossier1
    [1] => Array
        (
            [a] => dossier2
            [b] => dossier3
        )

)

Mais ce qui est plus intéressant c'est que vous pouvez dire "Je veux que le fichier [0] aille dans le dossier 1 mais que les fichiers [1][a] et [1][b] aillent dans le dossier 2.".

Au lieu d'avoir un tableau avec [a] et [b] vous avez juste une chaine avec le nom du dossier.
Vous écrivez simplement :

Array
(
    [0] => dossier1
    [1] => dossier2
)

Sympa non ?
De même vous pouvez vouloir mettre vos fichiers dans le même dossier auquel cas vous n'avez même plus besoin de tableau, indiquez juste le nom du dossier.

Ce fonctionnement marche peut importe le niveau de profondeur du tableau. Veillez simplement à vous assurer que les indices soient exacts.

Vous voulez savoir un truc marrant ? Le booléen pour savoir si on écrase ou pas le fichier ? Et bien il fonctionne comme le tableau des dossiers !
Vous pouvez grouper ou détailler l'écrasement de vos fichiers !

En conclusion si vous ne voulez pas vous prendre la tête à savoir si il y a un ou plusieurs fichiers sur le même name, utilisez "uploads" avec un dossier, le booléen pour savoir si on écrase les fichiers et il fera le reste.

Présentation de méthodes internes

Cette classe possède 2 méthodes d'une importance capitale que je me doit de vous détailler.

function nomSimple($n)

Cette méthode altère une chaine de caractères ou dans le cas qui nous intéresse, un nom de fichier.
Elle altère une chaine de manière à la "simplifer" de plusieurs fioritures potentielles qui sont potentiellement problématiques. J'ai d'ailleurs fait l'expérience de pas mal d'erreurs pas potentielles du tout dont je me serais bien passé, notamment avec les accents et les encodages.

Les opérations qu'elle effectue :

  1. Elle définie un caractère de remplacement pour les espaces blancs (ici le tiret "-").
  2. Elle retire le caractère de remplacement des espaces par un espace (si si, vous allez voir il n'y a pas de problème).
  3. Elle remplace TOUS les GROUPES d'espaces par LE caractère de remplacement des espaces (en partant de "a - b" on obtient avec l'étape 2. "a-b" et "a---b" sans l'étape 2)
  4. elle retire les accents que l'on connait, en remplaçant non pas les caractères en eux-mêmes mais leur entité html (exemple : 'é' et 'è', on remplace '&eacute;' et '&egrave;' par 'e'). Les entités trop complexes seront remplacées par le tiret bas "_".
  5. Elle remplace les derniers caractères complexes par un tiret bas "_".
  6. Elle met la chaine en minuscules.

Cette opération est appliquée sur le nom des fichiers envoyés, elle est destinée à simplifier les noms de fichier pour une utilisation ultérieure.

function unique($nom, $dossier)

Cette méthode retourne un nom de fichier qui n'existe pas dans le dossier $dossier en se basant sur le nom de fichier $nom. Lorsque vous demandez à ne pas écraser un fichier existant, cette méthode sera appelée pour trouver un nom qui n'est pas pris.

Pourquoi je vous parle de cette méthode ? Parce-que votre serviteur s'est bien embêté à vous fournir la possibilité de renommer vos fichiers selon vos masques.

Concrètement vous pourrez placer le numéro du fichier où vous voulez.

La liste des masques doit être présenté sous la forme d'un tableau où l'indice est le masque et la valeur le texte à remplacer.
Les masques présents par défaut sont :
Array(
    '#^(.*)\.([^.]*)$#' => '$1(%s).$2',
    '#^(.+)$#' => '$1(%s)'
);


Concrètement "fichier.txt" devient "fichier(1).txt" (masque 1) alors que ".fichier" devient ".fichier1" (masque 2).
Dès qu'un masque est trouvé le remplacement est appliqué. N'oubliez pas le "%s" dans le remplacement il n'agit du numéro de fichier.

Résultats

Maintenant vous avez appuyé sur "GO" et que se passe t-il ?!

Vos fichiers ont été envoyés sur le serveur si tout c'est bien passé, sinon il aura essayé et vous aura envoyé une erreur.

Les méthodes "upload" et "uploads" retournent des résultats assez intéressants, voyez plutôt :

// données récupérées de $r1 = $up->upload("monFichier", dossier);
UplObjet Object
(
    [input] => monFichier
    [nom] => Fichier - accentué.txt
    [dossier] => C:\wamp\www\SCRIPTS\uploader/fichiers
    [erreur] =>
    [code] => ok
    [message] => Le fichier a bien été envoyé.
    [indices] => Array
        (
        )

    [ecraser] =>
    [type] => text/plain
    [nomReel] => fichier-accentue.txt
    [taille] => 312
)

// données récupérées de $r2 = $up->uploads("monFichier2", dossier);
Array
(
    [0] => UplObjet Object
        (
            [input] => monFichier2
            [nom] =>
            [dossier] => C:\wamp\www\SCRIPTS\uploader/fichiers
            [erreur] => 1
            [code] => no-name
            [message] => Le fichier n'existe pas.
            [indices] => Array
                (
                    [0] => 0
                )

            [ecraser] =>
            [type] =>
            [nomReel] =>
            [taille] => 0
        )

    [1] => UplObjet Object
        (
            [input] => monFichier2
            [nom] =>
            [dossier] => C:\wamp\www\SCRIPTS\uploader/fichiers
            [erreur] => 1
            [code] => no-name
            [message] => Le fichier n'existe pas.
            [indices] => Array
                (
                    [0] => 1
                    [1] => a
                )

            [ecraser] =>
            [type] =>
            [nomReel] =>
            [taille] => 0
        )

    [2] => UplObjet Object
        (
            [input] => monFichier2
            [nom] =>
            [dossier] => C:\wamp\www\SCRIPTS\uploader/fichiers
            [erreur] => 1
            [code] => no-name
            [message] => Le fichier n'existe pas.
            [indices] => Array
                (
                    [0] => 1
                    [1] => b
                )

            [ecraser] =>
            [type] =>
            [nomReel] =>
            [taille] => 0
        )

)

Que c'est compliqué ! J'en conviens, j'ai potentiellement surchargé les informations retournées. ^^
Mais observons plutôt :

Le premier retour est un objet de type "UplObjet", les méthodes qui le concerne ne concernent que lui, donc je n'en parlerai pas, voyez la doc. En revanche ses attributs sont assez intéressants :

  1. input : le "name" qui a été utilisé pour envoyer le fichier
  2. nom : le nom originel du fichier
  3. dossier : le dossier dans lequel le fichier a été envoyé
  4. erreur : booléen signifiant "Il y a eu une erreur" lorsque celui-ci est Vrai
  5. code : le code du message retourné
  6. message : le texte correspondant au code de message
  7. indices : les indices du "name" pour ce fichier
  8. ecraser : booléen signifiant "On a demandé à écraser le fichier en cas de nom identique" lorsque celui-ci est Vrai
  9. type : type du fichier, rien de plus que $_FILES['monFichier']['type']
  10. nomReel : nom que possède en réalité le fichier
  11. taille : taille du fichier en octets, rien de plus que $_FILES['monFichier']['size']

Le second retour est un tableau qui contient 3 UplObjet, car il a compter l'envoi de 3 fichiers pour un seul "name".

Il y a beaucoup d'informations c'est vrai. Mais vous pouvez avec un seul de ces objets : voir le détail d'une erreur, comment le fichier a été envoyé, où il a été envoyé, quel est son nom réel, quel était son nom d'origine ...
 

Conclusion

J'espère avoir avoir été assez clair dans l'ensemble. Cette classe est complexe mais c'est pour pouvoir l'utiliser de manière simple.

Je vous met la classe en archive, avec le fichier d'index de test.
Remarque : j'utilise 2 fonctions personnelles dont les détails figurent sur ce site.

Je suis ouvert aux améliorations et aux commentaires constructifs, merci de me poster vos remarques et questions diverses.

Bonne continuation à tous et à bientôt !


- page 1 de 2