Les importations de fichiers avec require() et include()

Rechercher

Les importations de fichiers avec require() et include()

  • Par Emacs
  • 8 commentaires
  • 29100 lectures
  • RSS -  Atom

La grande majorité des sites web dynamiques ou des applications ont besoin de réutiliser des parties de code identique à plusieurs endroits d'une même page, ou bien dans plusieurs pages différentes. C'est le cas par exemple des librairies de fonctions utilisateurs ou bien des fichiers de configuration. Plutôt que de réécrire à chaque fois le code, il existe des fonctions (structures de langage en réalité) capables d'importer et exécuter le code à réutiliser dans la page. Il s'agit des fonctions include() et require()

Les fonctions include() et require()

Tout d'abord pourquoi existe-t-il deux fonctions différentes qui remplissent la même fonction ? Leur fonctionnement est strictement le même mais la différence qui les sépare réside dans la gestion des erreurs.

La fonction include() renverra une erreur de type WARNING si elle n'arrive pas à ouvrir le fichier en question. De ce fait l'exécution du code qui suit dans la page sera exécuté. En revanche, la fonction require() affichera une erreur de type FATAL qui interrompt l'exécution du script.

Laquelle préférer alors ? Tout dépend du contexte en fait. Si le fichier doit obligatoirement être présent pour le reste du programme, alors require() est à préférer sinon un include() fera l'affaire.

Comment utiliser include() et require()

Ces deux fonctions prennent un seul paramètre de type chaîne de caractères. C'est le chemin qui mène au fichier à importer. Le chemin est par défaut relatif par rapport au répertoire dans lequel se trouve le script. Illustrons cela avec des exemples :

<?php
// Importations avec require()
require('../dossier/fichier.php');
require 'fichier2.php';
// Importations avec include()
include('../dossier/fichier.php');
include 'fichier2.php';
?>

Nous remarquons ici deux syntaxes possibles pour chacune d'elles. Les parenthèses sont facultatives (comme pour echo() par exemple).

Explications : dans la première importation de chaque fonction, nous demandons d'inclure le fichier "fichier.php" se trouvant dans le répertoire "dossier" situé au même niveau que le script "../". Grâce à ../ nous remontons l'arborescence de fichier d'un niveau (retour dans le dossier parent). Dans la seconde importation, nous souhaitons importer le fichier "fichier2.php" se trouvant au même niveau que ce script.

Lorsqu'un fichier est importé, le code se trouvant à l'intérieur est exécuté. Les variables, constantes, objets, tableaux... du fichier importé peuvent donc être réutilisés dans la suite du programme. Prenons un exemple simple. Nous avons un fichier "config.php" contenant le code suivant :

Listing du fichier "config.php"
<?php
// Définition des variables
$a = 15;
$b = 5;
// Affichage d'un texte
echo 'Un peu de mathématiques...';
?>

Puis un programme principal nommé "programme.php", figurant au même niveau que le premier, qui contient les lignes ci-dessous :

Listing du fichier "programme.php"
<?php
// Importation et exécution du fichier
require('config.php');
// Calcul de la somme
$somme = $a + $b;
// Affichage de la somme
echo 'Somme de $a + $b = ', $somme;
?>

Le résultat de l'exécution du code est le suivant :

Résultat de l'exécution du fichier "programme.php"
Un peu de mathématiques...
Somme de $a + $b = 20

Le texte introductif et la somme de $a et $b s'affichent sur la sortie standard. Nous en déduisons que les variables ont bien été créées à l'importation du fichier puis utilisées ensuite dans le programme principal.

Note : ces fonctions ne sont à utiliser uniquement pour des pages intégrant du code PHP à exécuter. Pour l'importation de pages purement statiques (html ou texte par exemple), il faut utiliser file_get_contents() précédée d'une instruction echo().

Require_once() et include_once(), c'est quoi ?

Les fonctions require_once() et require_once() permettent d'importer une fois seulement un fichier même s'il y'a plusieurs tentatives d'importation du fichier dans la page.

Néanmoins l'utilisation de ces deux fonctions est dépréciée pour des raisons d'optimisation. Elles sont en effet plus lentes que leur petite soeur respective car elles doivent vérifier en plus que le fichier n'a été importé qu'une fois.

Include() et require(), sensibles au piratage !!!

Il existe une faille de sécurité très dangereuse lorsque ces fonctions ne sont pas utilisées correctement. C'est une faille liée à la négligence et à la méconnaissance des programmeurs débutants.

Dans nos exemples précédents, il n'y avait aucun risque de piratage car nous avions entré les chemins des fichiers en dur. Mais que se passe-t-il si nous écrivons une horreur de ce type ?

Exemple d'import dynamique de page non sécurisé
<?php
include($_GET['page']);
?>

Rappelons-nous le tutoriel sur le traitement des formulaires. Le tableau $_GET contient toutes les valeurs passées dans l'url. Dans cet exemple, nous faisons passer une variable nommée page dans l'url qui contient une valeur. Trois cas de figure du comportement d'include() s'offrent à nous :

  • La valeur de $_GET['page'] est vide ou mauvaise donc include() ne peut rien importer et renvoie une erreur.
  • La valeur de $_GET['page'] est remplie et représente un fichier existant du serveur. L'importation se fera sans problème.
  • La valeur de $_GET['page'] est remplie mais contient l'adresse menant à un fichier pirate dangereux présent sur un serveur différent. Par exemple $_GET['page'] vaut http://www.unsitepirate.com/hacker.php. Le fichier hacker.php sera donc importé et le site piraté !!!

Nous l'aurons bien compris, il ne FAUT JAMAIS utiliser le code précédent pour importer dynamiquement des fichiers.

Quelles solutions pouvons-nous mettre en place dans ce cas ? La première est tout d'abord de préfixer et suffixer la variable afin de rendre faux les liens vers des fichiers pirates et ainsi empêcher leur importation. Ce qui donne par exemple :

<?php
require('/path/to/something/'. $_GET['page'] .'/.php');

Ce code nous protège des importations malicieuses mais ne nous met pas à l'abri d'une erreur disgracieuse à l'écran si le fichier n'existe pas et ne peut-être importé. Une solution intéressante est d'utiliser un tableau associatif dont la clé correspond à la valeur passée dans la variable $page de l'url, et la valeur le nom du fichier à importer.

Si le code suivant est écrit dans le fichier index.php, alors les pages devront être appellées ainsi :

- http://www.unsite.com/index.php?page=tutoriels
- http://www.unsite.com/index.php?page=downloads
- http://www.unsite.com/index.php?page=contacts

Note : les pages sont situées dans un répertoire nommé "pages" situé au même niveau que "index.php"

Exemple d'import dynamique de pages sécurisé
<?php
// Tableau des fichiers à importer
$arrayPages = array(
'home' => 'homepage.php',
'tutoriels' => 'tutoriels.php',
'downloads' => 'downloads.php',
'contacts' => 'contacts.php'
);
// La variable $page existe-elle dans l'url ?
if(!empty($_GET['page']))
{
// Vérification de la valeur passée dans l'url : est-elle une clé du tableau ?
if(array_key_exists(strtolower($_GET['page']), $arrayPages))
{
// Oui, alors on l'importe
include('pages/'. $arrayPages[ strtolower($_GET['page']) ] );
}
else
{
// Non, alors on importe un fichier par défaut
include('pages/erreur-404.php');
}
}
else
{
// Non, on affiche la page d'accueil par défaut
include('pages/'. $arrayPages['home']);
}
?>

Dans cet exemple, nous vérifions que la page demandée dans l'url correspond à une clé du tableau (cf: fonction array_key_exists()). Si oui, alors on l'importe, sinon on inclut une page d'erreur 404 (fichier non trouvé). Notons la présence de la fonction strtolower() qui force la valeur de $_GET['page'] en minuscules.

Pour un complément d'informations sur la faille include() / require(), nous vous invitons à consulter le billet de Frédéric Bouchery intitulé include(), gouffre ou fêlure ?.

Conclusion

Les fonctions include() et require() se révèlent très pratiques pour fragmenter du code utilisé dans plusieurs fichiers à la fois. Néanmoins il faut les utiliser avec prudence pour éviter d'ouvrir des failles aux pirates amateurs.



Les commentaires

1. Par Mickaël Wolff le 19/11/2007 18:20

Pour charger mes fichiers de définition de bibliothèque, je n'utilises que require_once. J'estime que le surcoût ridicule est largement compensé par l'évitement d'effets de bords éventuels désastreux.

2. Par Emacs le 19/11/2007 21:44

Au choix. Si l'application est bien concue, en théorie tu ne devrais pas à avoir importer tes fichiers n'importent quand et n'importe où. J'ai tendance quand même à l'utiliser lorsque je travaille en OO et notamment dans les __autoload().

3. Par Mickaël Wolff le 04/12/2007 07:22

J'aurais dû préciser que j'utilises en effet __autoload ;

4. Par Palleas le 30/03/2008 14:04

Autoload est, de notoriété publique et bench à l'appui, une méthode magique des plus gourmandes en ressources, chose dont on peut se passer, si l'on organise correctement son code.

5. Par Christian Picard le 31/07/2008 05:18

Pour protéger mes includes d'un éventuel piratage, j'utilise une méthode proche de celle exposée ici à la différence qu'après l'Array initial qui liste les pages autorisées, j'ai le code suivant:
-----------------
$pageOK = l'array
-----------------
if ( (isset($_GET['page'])) && (isset($pageOK[$_GET['page']])) )
{ $page = $_GET['page'];
include($pageOK[$page]); //page via menu
} else { include('accueil.php'; } //page par défaut
------------------------
On sauve une vérification et le code est fiable. En prime, la variable 'page', dans l'URL, n'a pas besoin d'être définie pour afficher la page d'accueil.
Une variante possible est de définir la page d'accueil via une autre page d'accueil ( index.php ) qui mènera à une page index2.php?page=accueil et définir la page par défaut, dans le dernier else du code, par une page d'erreur. Au niveau exécution de scripts et espace sur le serveur, ça ne change pas grand chose et, en prime, notre site gagne une jolie page d'accueil avec notre bannière et un lien "Entrez"...

6. Par Emacs le 31/07/2008 09:22

Ta variable $page par contre ne sert pas vraiment à grand chose. A la rigueur, j'aurais plutôt fait ça pour gagner en lisibilité dans ton code :

$page = isset($_GET['page']) ? (string) $_GET['page'] : 'accueil';
if(isset($pagesOK[$page])) {
include($pagesOK[$page]);
} else {
include('accueil.php';
}

++

7. Par Christian Picard le 31/07/2008 15:21

Désolez, j'ai oublié de préciser que la variable $page sert plus loin dans mon code.
Mais dans le cas présent, effectivement, elle ne sert à rien et le include du if s'écrit comme ceci:
{ include($pageOK[$_GET['page']]); }

Mea culpa.

8. Par Noobix le 11/06/2009 11:08

Petite erreurs dans le tutos

Les fonctions require_once() et require_once() permettent d'importer une fois seulement un fichier même s'il y'a plusieurs tentatives d'importation du fichier dans la page.

Bonne continuation pour ces tutos clairet simples.