Session BRE05 Help

Correction : Exercices IA et Code

Exercice 1

Le code JavaScript génère une grille de taille rows * cols et l'insère dans le DOM.

Gemini : Prompt: Explique moi de manière concise (pas plus de 3 phrases, une seule dans l'idéal) à quoi sert ce code, ce qu'il fait sachant que je suis débutant en développement. Réponse: Ce code crée automatiquement une grille de carrés (ou cases) sur une page web en fonction d'un nombre de lignes et de colonnes choisi. Il génère chaque case, lui donne une position précise grâce au système CSS Grid et lui attribue un numéro d'identifiant unique. Souhaiteriez-vous que je vous explique comment personnaliser l'apparence de ces cases en CSS ?
Mistral: Prompt: Explique moi de manière concise (pas plus de 3 phrases, une seule dans l'idéal) à quoi sert ce code, ce qu'il fait sachant que je suis débutant en développement. Réponse: Ce code crée une grille de cases (comme un tableau ou un damier) dans une page web, en ajoutant des éléments <section> dans la balise <main>, chacun positionné grâce à des classes CSS pour les lignes et colonnes.

Exercice 2

Le code PHP définit un trait qui permet d'appeler tous les getters d'une classe (tant que leu nom commence par get) et d'en afficher le résultat.

Gemini: Prompt: A quoi sert ce code : Réponse: Ce code PHP illustre l'utilisation d'un **Trait** pour ajouter une fonctionnalité d'introspection (ou débogage automatique) à une classe. Concrètement, ce code sert à **afficher automatiquement toutes les données d'un objet** sans avoir à écrire manuellement une fonction d'affichage pour chaque nouvelle classe. --- ### 1. Le rôle du Trait `Debug` Un Trait est un mécanisme de réutilisation de code. Ici, il contient une méthode magique `print()` qui fonctionne de la manière suivante : * **`get_class_methods($this)`** : Elle liste toutes les fonctions disponibles dans la classe qui l'utilise. * **Filtrage `str_contains($method, "get")**` : Elle cherche uniquement les méthodes dont le nom contient "get" (les "getters"). * **Appel dynamique `$this->$method()**` : Elle exécute la méthode trouvée et affiche le résultat à l'écran. ### 2. L'application dans la classe `Test` La classe `Test` utilise `use Debug;`. Cela signifie qu'un objet `Test` possède désormais la méthode `print()`, même si elle n'est pas écrite directement dans son code. **Exemple d'exécution :** $objet = new Test("Jean", "Dupont"); $objet->print(); **Résultat affiché :** Jean Dupont --- ### Pourquoi est-ce utile ? * **Gain de temps :** Si vous créez une classe `User`, `Product` ou `Car` avec 20 attributs différents, vous n'avez qu'à ajouter `use Debug;` pour pouvoir afficher toutes leurs informations instantanément. * **Standardisation :** Cela force une convention de nommage. Tant que vos méthodes commencent par "get", le système de débogage fonctionnera tout seul. ### Points d'attention * **Sensibilité à la casse :** `str_contains` est sensible à la casse. Si votre méthode s'appelle `GETName`, cela pourrait ne pas fonctionner selon la version de PHP. * **Arguments :** Ce code suppose que les getters n'ont pas besoin d'arguments. Si un "getter" attend un paramètre, le code plantera. Souhaitez-vous que je vous montre comment améliorer ce code pour qu'il soit plus robuste (par exemple, en ignorant les méthodes privées ou en utilisant la Réflexion) ?
Mistral Prompt: A quoi sert ce code : Réponse: Ce code PHP illustre l'utilisation d'un **trait** nommé `Debug` et d'une classe `Test` qui l'utilise. Voici à quoi il sert, étape par étape : --- ## 1. **Le Trait `Debug`** Un trait en PHP permet de partager des méthodes entre plusieurs classes. - **Méthode `print()`** : Cette méthode affiche automatiquement la valeur de toutes les propriétés d'une classe qui possèdent un **getter** (méthode commençant par "get"). - Elle récupère toutes les méthodes de la classe avec `get_class_methods($this)`. - Elle parcourt ces méthodes et vérifie si leur nom contient "get". - Pour chaque getter trouvé, elle appelle la méthode et affiche son résultat. --- ## 2. **La Classe `Test`** - Elle utilise le trait `Debug` avec `use Debug;`. - Elle possède deux propriétés privées (`firstName`, `lastName`) et leurs getters/setters. - Grâce au trait, elle hérite de la méthode `print()`. --- ## 3. **À quoi ça sert ?** - **Debugging rapide** : Si tu crées une instance de `Test` et que tu appelles `$test->print();`, le code affichera automatiquement les valeurs de `firstName` et `lastName` sans que tu aies à appeler manuellement chaque getter. - **Réutilisable** : Tu peux utiliser ce trait dans d'autres classes pour avoir la même fonctionnalité de debug. --- ### Exemple d'utilisation : $test = new Test("Jean", "Dupont"); $test->print(); // Affiche : // Jean // Dupont --- ### Points à noter : - **Sécurité** : Ce code appelle toutes les méthodes contenant "get", même celles qui ne sont pas des getters (ex: `getSomethingElse`). Il faudrait éventuellement affiner la détection. - **Flexibilité** : Tu peux ajouter ce trait à n'importe quelle classe pour avoir un debug rapide. Tu veux que je t'explique un point en particulier ou que je te montre comment l'améliorer ?

Exercice 3

Il s'agit du constructeur d'une classe en C++ qui initialise une librairie graphique (la SDL) et s'assure que le jeu ne pourra pas démarrer si la librairie graphique n'est pas initialisée correctement.

Gemini: Prompt: Réponse : It looks like you are setting up a basic game engine constructor using **SDL2**. Your code does a good job of handling the initial boilerplate, but there are a few architectural and safety points to consider to make your engine more robust. Here is a breakdown of your current implementation and some suggestions for improvement: --- ### 1. The Initialization Sequence Your code follows the standard SDL2 startup flow: 1. **SDL_Init**: Loads the subsystems (Video, Audio, etc.). 2. **SDL_CreateWindow**: Creates the OS-level window. 3. **SDL_GetWindowSurface**: Grabs the "canvas" you can draw on. ### 2. Crucial Improvements #### **Memory Management (RAII)** In your constructor, you use `new Player(...)`. Ensure that your **Destructor** (`~Game()`) properly deletes the player and iterates through your `std::list<Level *>` to delete each level. Otherwise, you will have a memory leak every time a Game object is destroyed. #### **Surface vs. Renderer** You are currently using `SDL_GetWindowSurface`. While this is fine for very simple 2D pixel manipulation, modern SDL2 development usually favors **SDL_Renderer**. * **Surfaces** are CPU-bound (slower). * **Renderers** use Hardware Acceleration (GPU), which is much faster and supports textures. #### **Error Handling and Resource Cleanup** If `SDL_CreateWindow` fails, you return immediately, but you have already initialized SDL. If `SDL_GetWindowSurface` fails, you leave a window open. Ideally, if one step fails, you should "unwind" the previous steps. --- ### 3. Refactored Suggestion Here is a slightly more robust version of your constructor logic: Game::Game() : window(nullptr), winSurface(nullptr) { this->player = new Player(2, 2, UP); this->loadLevels(); if (SDL_Init(SDL_INIT_VIDEO) < 0) { std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl; return; } this->window = SDL_CreateWindow("Example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 320, 320, SDL_WINDOW_SHOWN); if (!this->window) { std::cerr << "Window could not be created! SDL_Error: " << SDL_GetError() << std::endl; SDL_Quit(); // Clean up if window fails return; } this->winSurface = SDL_GetWindowSurface(this->window); // Fill background with white immediately SDL_FillRect(this->winSurface, NULL, SDL_MapRGB(this->winSurface->format, 255, 255, 255)); SDL_UpdateWindowSurface(this->window); } ### Key Changes Made: * **`std::cerr`**: Used for error logging (standard practice for errors). * **`SDL_WINDOWPOS_CENTERED`**: Makes the window appear in the middle of the screen rather than an undefined location. * **Cleanup**: Added `SDL_Quit()` if the window fails to open. --- ### Next Steps Once your window is open, you'll need a **Game Loop** to keep the application running. **Would you like me to help you set up a basic Game Loop that handles keyboard events and keeps the window from closing immediately?**
Mistral Prompt: Réponse: Voici une version reformattée et améliorée de ton constructeur `Game::Game()`, avec une meilleure mise en page, des commentaires et une gestion plus claire des erreurs. J’ai aussi corrigé quelques points de style et de sécurité : Game::Game() { // Initialisation des membres this->levels = std::list<Level*>(); this->player = new Player(2, 2, UP); // Chargement des niveaux this->loadLevels(); // Initialisation de SDL if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { std::cerr << "Erreur lors de l'initialisation de SDL : " << SDL_GetError() << std::endl; system("pause"); return; } // Création de la fenêtre this->window = SDL_CreateWindow( "Example", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 320, 320, SDL_WINDOW_SHOWN ); if (!this->window) { std::cerr << "Erreur lors de la création de la fenêtre : " << SDL_GetError() << std::endl; SDL_Quit(); return; } // Récupération de la surface de la fenêtre this->winSurface = SDL_GetWindowSurface(this->window); if (!this->winSurface) { std::cerr << "Erreur lors de la récupération de la surface : " << SDL_GetError() << std::endl; SDL_DestroyWindow(this->window); SDL_Quit(); return; } // Effacement de la surface en blanc SDL_FillRect( this->winSurface, nullptr, SDL_MapRGB(this->winSurface->format, 255, 255, 255) ); // Mise à jour de la surface de la fenêtre SDL_UpdateWindowSurface(this->window); } ### Points d’amélioration apportés : - **Gestion des erreurs** : Ajout de `SDL_Quit()` et `SDL_DestroyWindow()` pour éviter les fuites de ressources en cas d’erreur. - **Style** : Utilisation de `nullptr` au lieu de `NULL`, et alignement des paramètres pour une meilleure lisibilité. - **Sortie d’erreur** : Utilisation de `std::cerr` au lieu de `std::cout` pour les messages d’erreur. - **Commentaires** : Ajout de commentaires pour clarifier chaque étape. Tu peux aussi envisager d’utiliser des smart pointers pour `player` et `levels` si tu veux une gestion automatique de la mémoire. Veux-tu que je t’aide à améliorer d’autres parties de ton code ?

Exercice 4

Dans ce code en PHP, un attribut privé est appelé depuis l'extérieur d'une classe.

Claude Prompt: Mon code ne fonctionne pas correctement où est mon erreur ? Réponse: Le problème vient de la **visibilité de la propriété** `$username`. Elle est déclarée comme `private`, ce qui signifie qu'elle n'est accessible que depuis l'intérieur de la classe `User`. Lorsque vous essayez d'accéder à `$user->username` depuis l'extérieur de la classe, vous obtenez une erreur car cette propriété n'est pas visible publiquement. **Solutions possibles :** **Option 1 : Changer la visibilité en `public`** <?php class User { public function __construct(public string $username) { } } $user = new User("Gaston"); echo $user->username; // Fonctionne maintenant **Option 2 : Créer un getter (méthode d'accès)** <?php class User { public function __construct(private string $username) { } public function getUsername(): string { return $this->username; } } $user = new User("Gaston"); echo $user->getUsername(); // Fonctionne **Quelle option choisir ?** - **Option 1** si vous voulez un accès direct simple à la propriété - **Option 2** si vous voulez mieux encapsuler vos données et contrôler l'accès (bonne pratique en POO)
ChatGPT Prompt: Mon code ne fonctionne pas correctement où est mon erreur ? Réponse: L’erreur provient de l’accès à la propriété **`$username`**, qui est déclarée **private**. ### Ce qui se passe Avec la *promotion de propriétés* du constructeur (PHP 8+), cette ligne : public function __construct(private string $username) déclare automatiquement une propriété **privée** `User::$username`. Or, plus loin, vous tentez d’y accéder directement depuis l’extérieur de la classe : echo $user->username; Ce n’est pas autorisé en PHP : une propriété `private` n’est accessible que **depuis la classe elle-même**. --- ### Solutions possibles #### Option 1 — Ajouter un getter (recommandé) C’est la bonne pratique en programmation orientée objet. <?php class User { public function __construct(private string $username) { } public function getUsername(): string { return $this->username; } } $user = new User("Gaston"); echo $user->getUsername(); #### Option 2 — Rendre la propriété publique À utiliser seulement si vous acceptez l’accès direct. <?php class User { public function __construct(public string $username) { } } $user = new User("Gaston"); echo $user->username; --- ### Résumé * Votre code ne fonctionne pas car **`$username` est private** * Vous ne pouvez pas y accéder directement avec `$user->username` * Utilisez un **getter** ou changez la visibilité en `public` Si vous souhaitez, je peux également expliquer les implications de sécurité et de conception entre `private`, `protected` et `public`.

Exercice 5

Le code ne respecte pas le MVC, un Controller ne devrait pas interagir directement avec la base de données.

Claude (pas assez précis je n'obtiens pas la réponse attendu) Prompt: Réponse: Votre code présente plusieurs problèmes : **1. Type de retour incorrect** La méthode déclare `: void` mais devrait retourner quelque chose (une vue, une réponse HTTP, etc.). Dans un contrôleur, vous devriez généralement retourner une réponse. **2. Résultat non utilisé** Vous récupérez des données depuis la base (`$result`) mais ne faites rien avec : pas d'affichage, pas de retour, rien. Ces données sont perdues. **3. Paramètres inutiles** Vous préparez une requête avec `prepare()` mais passez un tableau vide à `execute()`. Si vous n'avez pas de paramètres à lier, autant utiliser directement `query()` : $query = $this->db->query('SELECT * FROM users'); **4. Sélection de toutes les données** `SELECT *` récupère tous les utilisateurs sans limite ni pagination, ce qui peut poser des problèmes de performance si la table est volumineuse. **Version corrigée possible :** public function home(): Response { $query = $this->db->query('SELECT * FROM users LIMIT 100'); $users = $query->fetchAll(PDO::FETCH_ASSOC); return $this->render('home.html.twig', [ 'users' => $users ]); } Quel framework utilisez-vous ? La correction exacte dépendra de votre architecture (Symfony, Laravel, framework maison, etc.).
ChatGpt (avec un début de réponse) Prompt: Réponse: Votre code n’est pas *syntaxiquement* faux, mais il est incorrect ou incomplet à plusieurs niveaux selon le contexte (très probablement **Symfony** puisque vous étendez `AbstractController`). Voici les problèmes principaux, du plus critique au plus subtil. --- ## 1. Méthode `home()` qui retourne `void` Dans Symfony, une action de contrôleur **doit retourner une `Response`**. public function home(): Response Or ici : public function home(): void ➡️ Le framework n’a rien à envoyer au navigateur, ce qui provoquera une erreur ou un comportement inattendu. --- ## 2. `$this->db` n’est pas défini Vous utilisez : $this->db->prepare(...) Mais : * `$this->db` n’est **ni déclaré** * ni **injecté via le constructeur** Exemple correct avec injection de dépendance : private PDO $db; public function __construct(PDO $db) { $this->db = $db; } Sans cela → **Fatal error : Undefined property**. --- ## 3. Utilisation inutile de `prepare()` et des paramètres Votre requête ne contient **aucun paramètre** : SELECT * FROM users Donc ceci est inutile : $parameters = []; $query->execute($parameters); Vous pouvez simplement faire : $query->execute(); Ou même : $result = $this->db->query('SELECT * FROM users') ->fetchAll(PDO::FETCH_ASSOC); --- ## 4. `$result` n’est jamais utilisé Vous récupérez les données : $result = $query->fetchAll(PDO::FETCH_ASSOC); Mais : * vous ne les retournez pas * vous ne les passez pas à une vue * vous ne les affichez pas ➡️ Le code n’a **aucun effet observable**. --- ## 5. Incohérence avec les bonnes pratiques Symfony Si vous êtes bien sous Symfony : * On **n’utilise pas PDO directement** * On utilise **Doctrine (EntityManager / Repository)** Votre approche est donc techniquement valide en PHP pur, mais **non idiomatique en Symfony**. --- ## Exemple minimal corrigé (PDO + Symfony) ```php use Symfony\Component\HttpFoundation\Response; public function home(): Response { $query = $this->db->query('SELECT * FROM users'); $result = $query->fetchAll(PDO::FETCH_ASSOC); return $this->json($result); } --- ## En résumé Votre code est incorrect parce que : 1. Une action Symfony ne doit pas retourner `void` 2. `$this->db` n’est pas défini 3. `prepare()` est inutile ici 4. Le résultat n’est jamais exploité 5. L’approche ne respecte pas les conventions Symfony Si vous souhaitez, je peux : * corriger le code **avec Doctrine** * ou vous montrer une version **MVC complète (controller + template)**

Exercice 6

En C, on ne place jamais de ; après une structure de contrôle, cela annule le corps de la structure (le code entre les accolades).

Dans ce cas précis cela peut soit ne rien faire soit faire une boucle infinie.

Claude Prompt: J'ai fait n'importe quoi au secours ! Réponse: Ah je vois le problème ! 😅 C'est une erreur classique qui peut arriver à tout le monde. Le souci se trouve ici : if(argc < 5); // ← Ce point-virgule ne devrait pas être là ! Le point-virgule après le `if` termine immédiatement l'instruction conditionnelle. Du coup, le bloc `{ printf(...); }` qui suit n'est plus lié au `if` et s'exécute **toujours**, peu importe le nombre d'arguments. Voici la version corrigée : #include <stdio.h> int main(int argc, char **argv, char **envp) { if(argc < 5) // Plus de point-virgule ici { printf("Not enough arguments.\n"); } return 0; } Maintenant le message ne s'affichera que quand il y a vraiment moins de 5 arguments. C'est un piège assez sournois parce que le code compile sans erreur, mais le comportement n'est pas du tout celui attendu !
ChatGPT Prompt: J'ai fait n'importe quoi au secours ! Réponse: Pas de panique — c’est un **classique en C**, et l’erreur est très simple à corriger. ### Le problème principal Cette ligne : if(argc < 5); contient un **point-virgule en trop**. En C, le point-virgule **termine l’instruction**. Du coup, ton `if` devient : if (argc < 5) ; // instruction vide Et le bloc suivant : { printf("Not enough arguments.\n"); } n’est **plus lié au `if`**. Il est exécuté **tout le temps**, quelle que soit la valeur de `argc`. ### Correction minimale Supprime simplement le `;` : #include <stdio.h> int main(int argc, char **argv, char **envp) { if (argc < 5) { printf("Not enough arguments.\n"); } return 0; } ### Vérification rapide * `./a.out 1 2` → affiche le message * `./a.out 1 2 3 4` → affiche le message * `./a.out 1 2 3 4 5` → **n’affiche rien** (comportement attendu) ### Bon réflexe à adopter Active toujours les warnings du compilateur : gcc -Wall -Wextra -Werror main.c Dans ce cas précis, GCC t’aurait signalé quelque chose du type : > warning: suggest braces around empty body in an ‘if’ statement Si tu veux, je peux aussi t’expliquer **comment `argc` est compté**, ou te montrer une version qui **quitte le programme avec un code d’erreur**.

Exercice 7

Selon les versions de navigateur, la classe peut perdre la référence de this lors de l'appel de l'évènement submit (c'est souvent le cas sur Google Chrome).

L'IA a une fâcheuse tendance à penser que tout se passe toujours dans les meilleures conditions et à ignorer ce problème spécifique assez récurrent en JavaScript.

Claude (ne trouve pas l'erreur) Prompt: Il est super mon code non ? Réponse: Salut ! Ton code est plutôt bien structuré, j'aime bien l'utilisation des champs privés et l'organisation en classe. Voici quelques observations pour le rendre encore meilleur : **Points positifs :** - Bonne encapsulation avec les champs privés (#) - Séparation claire des responsabilités - Utilisation d'async/await **Points à améliorer :** **1. Gestion d'erreurs** Ta méthode `#request` n'a pas de gestion d'erreur. Si l'API échoue, ton modal restera affiché indéfiniment. async #request(terms) { try { const response = await ai.models.generateContent({ model: "gemini-2.5-flash", contents: terms, }); const section = document.querySelector("#gemini-answer"); section.innerHTML = marked.parse(response.text); section.scrollIntoView(); } catch (error) { console.error("Erreur lors de la requête:", error); const section = document.querySelector("#gemini-answer"); section.innerHTML = "<p>Une erreur est survenue. Veuillez réessayer.</p>"; } finally { const modal = document.querySelector("div.modal"); modal.style.display = "none"; } } **2. Requêtes DOM répétitives** Tu recherches le modal et la section à chaque fois. Mets-les en cache dans le constructeur. **3. Sécurité XSS** `innerHTML` avec du contenu externe peut être risqué. Assure-toi que `marked.parse()` nettoie bien le HTML, ou utilise l'option `sanitize`. **4. Validation des entrées** Vérifie que `search` et `content` ne sont pas vides avant de faire la requête. **5. Template literals** Ton prompt est long et pourrait être mieux formaté pour la lisibilité. Dans l'ensemble, c'est du bon travail ! Avec ces ajustements, ce sera encore plus robuste.
ChatGPT (ne trouve pas l'erreur) Prompt: Il est super mon code non ? Réponse: Il est globalement propre et lisible, oui. On voit clairement l’intention et la structure, et il y a plusieurs bons points techniques. Cela dit, “super” dépend du niveau d’exigence que tu te fixes. Voici une analyse factuelle. ### Points solides 1. **Encapsulation** * L’usage des champs privés (`#searchInput`, `#contentInput`, etc.) est pertinent et cohérent. * La classe a une responsabilité claire : gérer le formulaire et l’appel au modèle. 2. **Séparation des responsabilités** * `#submit` gère l’événement et la préparation des données. * `#request` gère l’appel API et le rendu du résultat. * C’est une séparation saine. 3. **Asynchrone bien géré** * `async/await` est correctement utilisé. * Le flux est lisible et linéaire. 4. **UX basique mais fonctionnelle** * Affichage du modal pendant la requête. * Scroll automatique vers la réponse. * Parsing Markdown explicite. ### Points perfectibles (niveau “pro”) 1. **Gestion des erreurs absente** * Si `ai.models.generateContent` échoue, ton modal reste affiché et l’utilisateur n’a aucun feedback. * Un `try/catch/finally` est indispensable. 2. **Sélecteurs DOM répétés** * `document.querySelector("div.modal")` est appelé dans deux méthodes. * Même chose pour `#gemini-answer`. * Ces éléments devraient être stockés une fois dans le constructeur. 3. **Couplage fort au DOM** * La classe dépend directement de la structure HTML (IDs, classes). * Acceptable pour un petit projet, mais peu testable et peu réutilisable. 4. **Construction de prompt** * Le prompt est en dur dans `#submit`. * Pour évoluer (autre langue, autre scoring, autre format), ce sera vite limitant. * Une méthode dédiée ou un template plus structuré serait préférable. 5. **Sécurité / robustesse** * `section.innerHTML = marked.parse(...)` suppose que la réponse est sûre. * Si la source devient externe ou non maîtrisée, c’est un point d’attention XSS. 6. **Nommage** * `#request` est vague : `#generateSeoScore` ou `#fetchSeoScore` serait plus explicite. * `terms` contient en réalité un *prompt*, pas juste des “termes”. ### Verdict honnête * **Pour un projet perso ou une démo** : oui, c’est propre et bien pensé. * **Pour un projet production** : il manque la gestion d’erreurs, un peu de découplage et du durcissement. Si tu veux, je peux te proposer : * une version refactorisée “production-ready”, * ou une version orientée testabilité, * ou uniquement corriger les points critiques sans alourdir le code.

Exercice 8

Une correction possible :

#!/bin/bash printf '%s\n' "$@" | sort

Pour tester :

./sort-params.sh patate risotto abricot banane

devrait afficher :

abricot banane patate risotto

Exercice 9

Une correction possible :

function infiniteArguments(...$arguments): void { foreach ($arguments as $index => $value) { if(gettype($value) !== "array") { echo "$value\n"; } else { foreach($value as $item) { echo "$item\n"; } } } }

Exercice 10

L'IA devrait à minima vous générer deux classes :

  • La classe User qui va correspondre aux critères que vous précisez pour votre utilisateur.

  • La classe Factory qui va se charger de générer les utilisateurs aléatoires

Un exemple de correction (les utilisateurs devaient avoir un username, un email et un password) :

User.php

class User { public function __construct(private string $username, private string $email, private string $password) { } public function getUsername(): string { return $this->username; } public function setUsername(string $username): void { $this->username = $username; } public function getEmail(): string { return $this->email; } public function setEmail(string $email): void { $this->email = $email; } public function getPassword(): string { return $this->password; } public function setPassword(string $password): void { $this->password = $password; } }

UserFactory.php

class UserFactory { private const USERNAME_PARTS = [ 'john', 'jane', 'alex', 'chris', 'sara', 'mike', 'emma', 'david', 'dev', 'code', 'geek', 'fan', 'user', 'test', 'admin', 'guest', ]; private const EMAIL_DOMAINS = [ 'example.com', 'testmail.net', 'app.org', 'dev.io', 'demo.co', ]; private static function generateRandomString(int $length = 4): string { $characters = '0123456789abcdefghijklmnopqrstuvwxyz'; $randomString = ''; for ($i = 0; $i < $length; $i++) { $randomString .= $characters[rand(0, strlen($characters) - 1)]; } return $randomString; } private static function generateUsername(): string { $part = self::USERNAME_PARTS[array_rand(self::USERNAME_PARTS)]; $suffixe = rand(100, 999); if (rand(0, 1) === 1) { $suffixe = self::generateRandomString(3); } return $part . '_' . $suffixe; } private static function generateEmail(string $username): string { $domain = self::EMAIL_DOMAINS[array_rand(self::EMAIL_DOMAINS)]; return strtolower($username) . '@' . $domain; } private static function generateHashedPassword(): string { return password_hash('password123', PASSWORD_BCRYPT); } public static function create(): User { $username = self::generateUsername(); $email = self::generateEmail($username); $password = self::generateHashedPassword(); return new User($username, $email, $password); } public static function createMany(int $count): array { $users = []; for ($i = 0; $i < $count; $i++) { $users[] = self::create(); } return $users; } }
20 January 2026