Quand votre projet PHP passe de 5 fichiers à 50, le chaos s'installe vite. Comment organiser tout ça ? Comment éviter les conflits de noms ? Comment charger les classes automatiquement ? Ce guide vous accompagne pas à pas.
Le problème : un projet qui grandit
Au début, tout va bien. Vous avez quelques fichiers :
mon-projet/
├── index.php
├── User.php
├── Product.php
└── Database.php
Puis le projet grandit. Vous ajoutez des fonctionnalités. Soudain, vous avez :
mon-projet/
├── index.php
├── User.php
├── UserController.php
├── UserService.php
├── UserRepository.php
├── Product.php
├── ProductController.php
├── ... (50 autres fichiers)
Deux problèmes apparaissent :
- C'est le bazar : impossible de trouver rapidement un fichier
- Conflits de noms : que faire si vous avez deux classes
Userdifférentes ?
La solution : les namespaces
Un namespace (espace de noms), c'est comme un nom de famille pour vos classes. Deux personnes peuvent s'appeler "Marie", mais "Marie Dupont" et "Marie Martin" sont bien distinctes.
Déclarer un namespace
Créons une structure organisée :
src/
├── User/
│ ├── User.php
│ ├── UserController.php
│ └── UserService.php
└── Product/
├── Product.php
└── ProductController.php
Dans src/User/User.php :
<?php
namespace App\User; // On déclare le namespace
class User
{
private string $name;
private string $email;
public function __construct(string $name, string $email)
{
$this->name = $name;
$this->email = $email;
}
public function getName(): string
{
return $this->name;
}
}
Dans src/Product/Product.php :
<?php
namespace App\Product; // Namespace différent
class Product
{
private string $name;
private float $price;
public function __construct(string $name, float $price)
{
$this->name = $name;
$this->price = $price;
}
}
Règle d'or : Le namespace correspond au chemin du fichier.
App\User\User=src/User/User.php
Utiliser une classe avec son namespace
Il existe deux façons d'utiliser une classe :
1. Le nom complet (FQCN - Fully Qualified Class Name)
<?php
// On utilise le chemin complet
$user = new \App\User\User('Jean', 'jean@example.com');
$product = new \App\Product\Product('Clavier', 49.99);
C'est verbeux et pénible. Heureusement, il y a mieux.
2. Le mot-clé use (recommandé)
<?php
namespace App\Shop;
use App\User\User; // On "importe" la classe
use App\Product\Product;
class Cart
{
private User $user;
private array $products = [];
public function __construct(User $user)
{
$this->user = $user;
}
public function addProduct(Product $product): void
{
$this->products[] = $product;
}
}
Le code est maintenant lisible et les classes bien identifiées.
Gérer les conflits de noms avec les alias
Imaginez deux classes User différentes :
<?php
namespace App\Admin;
use App\User\User; // User classique
use App\Admin\User as AdminUser; // User admin avec un alias
class Dashboard
{
public function showUser(User $user): void
{
// Utilisateur normal
}
public function showAdmin(AdminUser $admin): void
{
// Administrateur
}
}
Le problème du chargement des fichiers
Avec des namespaces, vous avez des fichiers partout. Comment les charger ? La méthode naïve :
<?php
// index.php
require_once 'src/User/User.php';
require_once 'src/User/UserController.php';
require_once 'src/User/UserService.php';
require_once 'src/Product/Product.php';
require_once 'src/Product/ProductController.php';
// ... 50 lignes de require
C'est un cauchemar à maintenir. Chaque nouvelle classe = une ligne à ajouter.
L'autoloading : le chargement automatique
L'autoloading charge automatiquement les fichiers quand PHP rencontre une classe inconnue.
Autoloading manuel (pour comprendre)
<?php
// autoload.php
spl_autoload_register(function (string $className) {
// App\User\User devient src/User/User.php
$file = str_replace('\\', '/', $className) . '.php';
$file = str_replace('App/', 'src/', $file);
if (file_exists($file)) {
require_once $file;
}
});
Utilisation :
<?php
// index.php
require_once 'autoload.php'; // Un seul require !
use App\User\User;
use App\Product\Product;
$user = new User('Jean', 'jean@example.com'); // Chargé automatiquement
$product = new Product('Souris', 29.99); // Chargé automatiquement
Ça fonctionne, mais c'est fragile. Heureusement, il existe un outil professionnel.
Composer : la solution professionnelle
Composer est le gestionnaire de dépendances PHP. Mais même sans dépendances externes, il gère parfaitement l'autoloading.
Installation de Composer
# Sur Linux/Mac
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
# Vérifier l'installation
composer --version
Créer le fichier composer.json
À la racine de votre projet, créez composer.json :
{
"name": "mon/projet",
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
Cette configuration dit : "Tout ce qui commence par App\ se trouve dans le dossier src/".
Générer l'autoloader
composer dump-autoload
Composer crée un dossier vendor/ avec un fichier autoload.php optimisé.
Utilisation finale
<?php
// index.php
require_once __DIR__ . '/vendor/autoload.php'; // Une seule ligne !
use App\User\User;
use App\Product\Product;
use App\Shop\Cart;
// Tout se charge automatiquement
$user = new User('Marie', 'marie@example.com');
$product = new Product('Écran', 299.99);
$cart = new Cart($user);
$cart->addProduct($product);
PSR-4 : la convention à suivre
PSR-4 est le standard d'autoloading de PHP. Les règles sont simples :
- Un namespace = un dossier
- Une classe = un fichier
- Le nom du fichier = le nom de la classe +
.php
Exemple concret :
src/
├── User/
│ ├── User.php # namespace App\User; class User
│ ├── UserController.php # namespace App\User; class UserController
│ └── Repository/
│ └── UserRepository.php # namespace App\User\Repository; class UserRepository
└── Product/
└── Product.php # namespace App\Product; class Product
Exemple complet : une mini-application
Créons une structure de projet complète :
mon-projet/
├── composer.json
├── public/
│ └── index.php
└── src/
├── Entity/
│ └── User.php
├── Repository/
│ └── UserRepository.php
└── Service/
└── UserService.php
composer.json
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
src/Entity/User.php
<?php
namespace App\Entity;
class User
{
public function __construct(
private int $id,
private string $name,
private string $email
) {}
public function getId(): int { return $this->id; }
public function getName(): string { return $this->name; }
public function getEmail(): string { return $this->email; }
}
src/Repository/UserRepository.php
<?php
namespace App\Repository;
use App\Entity\User;
class UserRepository
{
private array $users = [];
public function __construct()
{
// Données de test
$this->users = [
1 => new User(1, 'Alice', 'alice@example.com'),
2 => new User(2, 'Bob', 'bob@example.com'),
];
}
public function find(int $id): ?User
{
return $this->users[$id] ?? null;
}
public function findAll(): array
{
return $this->users;
}
}
src/Service/UserService.php
<?php
namespace App\Service;
use App\Repository\UserRepository;
use App\Entity\User;
class UserService
{
public function __construct(
private UserRepository $repository
) {}
public function getUserById(int $id): ?User
{
return $this->repository->find($id);
}
public function getAllUsers(): array
{
return $this->repository->findAll();
}
}
public/index.php
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use App\Repository\UserRepository;
use App\Service\UserService;
// Tout se charge automatiquement !
$repository = new UserRepository();
$service = new UserService($repository);
// Récupérer un utilisateur
$user = $service->getUserById(1);
if ($user) {
echo "Utilisateur trouvé : " . $user->getName() . "\n";
}
// Lister tous les utilisateurs
foreach ($service->getAllUsers() as $user) {
echo "- " . $user->getName() . " (" . $user->getEmail() . ")\n";
}
Commandes Composer essentielles
# Générer/mettre à jour l'autoloader
composer dump-autoload
# Optimiser pour la production
composer dump-autoload --optimize
# Installer les dépendances (si vous en ajoutez)
composer install
# Mettre à jour les dépendances
composer update
Récapitulatif
- Namespaces : organisent vos classes et évitent les conflits
- use : importe une classe pour l'utiliser sans le chemin complet
- PSR-4 : namespace = dossier, classe = fichier
- Composer : gère l'autoloading automatiquement
- vendor/autoload.php : un seul require pour tout charger
Avec ces bases, vous pouvez maintenant créer des projets PHP bien structurés, maintenables et professionnels.