| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- <?php
- // Wrapper neutre global (fallback safe) — déclaré au niveau fichier
- if (!class_exists('NeutralTemplateWrapper')) {
- class NeutralTemplateWrapper
- {
- public static function id(): string { return 'neutral'; }
- public static function applyTemplate($map): void { /* no-op */ }
- public static function tileDefinitions(): array { return [['id'=>'empty','name'=>'Vide','color'=>'#ffffff']]; }
- }
- }
- if (!class_exists('TemplateFactory')) {
- class TemplateFactory
- {
- private static $cache = null;
- /**
- * Scanne le dossier mapModels pour découvrir les modèles disponibles
- * Retourne un tableau id => ['id'=>..., 'name'=>..., 'description'=>..., 'class'=>...]
- * @return array
- */
- public static function listAvailable(): array
- {
- if (self::$cache !== null) return self::$cache;
- // calculer le dossier mapModels à partir de la racine du projet
- $projectRoot = dirname(__DIR__, 3); // app/Models/Templates -> app/Models -> app -> project root
- $base = $projectRoot . '/mapModels';
- $results = [];
- if (is_dir($base)) {
- $dirs = scandir($base);
- foreach ($dirs as $d) {
- if ($d === '.' || $d === '..') continue;
- $modelJson = $base . '/' . $d . '/model.json';
- if (file_exists($modelJson)) {
- $raw = file_get_contents($modelJson);
- $meta = json_decode($raw, true) ?: [];
- $id = $meta['id'] ?? $d;
- $name = $meta['name'] ?? $id;
- $desc = $meta['description'] ?? '';
- // tenter de charger un fichier PHP depuis le dossier du modèle (sécurisé)
- $modelDir = realpath($base . '/' . $d);
- $entry = $meta['entry'] ?? 'template.php';
- $entryPath = $modelDir ? $modelDir . DIRECTORY_SEPARATOR . $entry : null;
- $loadedClass = null;
- $loadedFromModel = false;
- if ($entryPath && is_file($entryPath) && pathinfo($entryPath, PATHINFO_EXTENSION) === 'php') {
- // protection : s'assurer que le fichier est bien dans le dossier mapModels
- $realEntry = realpath($entryPath);
- if ($realEntry !== false && strpos($realEntry, realpath($base)) === 0) {
- // inclure le fichier. utiliser require_once pour éviter double inclusion
- try {
- $before = get_declared_classes();
- require_once $realEntry;
- $loadedFromModel = true;
- $after = get_declared_classes();
- // ne considérer que les classes nouvellement déclarées par ce fichier
- $new = array_diff($after, $before);
- // détecter automatiquement une classe qui implémente MapTemplateInterface
- foreach ($new as $decl) {
- if (is_subclass_of($decl, 'MapTemplateInterface')) {
- $loadedClass = $decl;
- break;
- }
- }
- // fallback : nom conventionnel défini dans le même fichier
- if ($loadedClass === null) {
- $canon = ucfirst($id) . 'Template';
- if (in_array($canon, $new, true)) $loadedClass = $canon;
- }
- } catch (Throwable $e) {
- // ne pas interrompre la discovery sur erreur de template utilisateur
- $loadedClass = null;
- }
- }
- }
- // Ne pas tenter de charger des classes locales : forcer l'usage exclusif des plugins dans mapModels
- // si $loadedClass est null, on laisse null (fallback géré par TemplateFactory::get)
- $results[$id] = [
- 'id' => $id,
- 'name' => $name,
- 'description' => $desc,
- 'class' => $loadedClass,
- 'entry' => $entryPath,
- 'loadedFromModel' => $loadedFromModel
- ];
- }
- }
- }
- // Always ensure neutral exists as fallback (no local PHP required)
- if (!isset($results['neutral'])) {
- $results['neutral'] = [
- 'id' => 'neutral',
- 'name' => 'Neutre',
- 'description' => 'Template neutre',
- 'class' => null,
- 'entry' => null,
- 'loadedFromModel' => false
- ];
- }
- self::$cache = $results;
- return $results;
- }
- /**
- * Retourne la classe du template demandé (ou NeutralTemplate)
- * @param string|null $id
- * @return string|null
- */
- public static function get(?string $id = null): ?string
- {
- $list = self::listAvailable();
- if ($id && isset($list[$id]) && !empty($list[$id]['class'])) return $list[$id]['class'];
- // si neutral a une classe fournie par mapModels, l'utiliser
- if (!empty($list['neutral']['class'])) return $list['neutral']['class'];
- // sinon retourner le wrapper neutre global
- return class_exists('NeutralTemplateWrapper') ? 'NeutralTemplateWrapper' : null;
- }
- }
- }
|