TemplateFactory.php 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. <?php
  2. if (!class_exists('TemplateFactory')) {
  3. class TemplateFactory
  4. {
  5. private static $cache = null;
  6. /**
  7. * Scanne le dossier mapModels pour découvrir les modèles disponibles
  8. * Retourne un tableau id => ['id'=>..., 'name'=>..., 'description'=>..., 'class'=>...]
  9. * @return array
  10. */
  11. public static function listAvailable(): array
  12. {
  13. if (self::$cache !== null) return self::$cache;
  14. $base = __DIR__ . '/../../mapModels';
  15. $results = [];
  16. if (is_dir($base)) {
  17. $dirs = scandir($base);
  18. foreach ($dirs as $d) {
  19. if ($d === '.' || $d === '..') continue;
  20. $modelJson = $base . '/' . $d . '/model.json';
  21. if (file_exists($modelJson)) {
  22. $raw = file_get_contents($modelJson);
  23. $meta = json_decode($raw, true) ?: [];
  24. $id = $meta['id'] ?? $d;
  25. $name = $meta['name'] ?? $id;
  26. $desc = $meta['description'] ?? '';
  27. // tenter de charger un fichier PHP depuis le dossier du modèle (sécurisé)
  28. $modelDir = realpath($base . '/' . $d);
  29. $entry = $meta['entry'] ?? 'template.php';
  30. $entryPath = $modelDir ? $modelDir . DIRECTORY_SEPARATOR . $entry : null;
  31. $loadedClass = null;
  32. $loadedFromModel = false;
  33. if ($entryPath && is_file($entryPath) && pathinfo($entryPath, PATHINFO_EXTENSION) === 'php') {
  34. // protection : s'assurer que le fichier est bien dans le dossier mapModels
  35. $realEntry = realpath($entryPath);
  36. if ($realEntry !== false && strpos($realEntry, realpath($base)) === 0) {
  37. // inclure le fichier. utiliser require_once pour éviter double inclusion
  38. try {
  39. require_once $realEntry;
  40. $loadedFromModel = true;
  41. // détecter automatiquement une classe qui implémente MapTemplateInterface
  42. foreach (get_declared_classes() as $decl) {
  43. if (is_subclass_of($decl, 'MapTemplateInterface')) {
  44. $loadedClass = $decl;
  45. break;
  46. }
  47. }
  48. // fallback : nom conventionnel
  49. if ($loadedClass === null) {
  50. $canon = ucfirst($id) . 'Template';
  51. if (class_exists($canon)) $loadedClass = $canon;
  52. }
  53. } catch (Throwable $e) {
  54. // ne pas interrompre la discovery sur erreur de template utilisateur
  55. $loadedClass = null;
  56. }
  57. }
  58. }
  59. // s'il n'y a pas de fichier dans mapModels, tenter la classe interne comme avant
  60. if ($loadedClass === null) {
  61. $classFile = __DIR__ . '/' . ucfirst($id) . 'Template.php';
  62. $className = ucfirst($id) . 'Template';
  63. if (file_exists($classFile)) {
  64. require_once $classFile;
  65. if (class_exists($className)) $loadedClass = $className;
  66. }
  67. }
  68. $results[$id] = [
  69. 'id' => $id,
  70. 'name' => $name,
  71. 'description' => $desc,
  72. 'class' => $loadedClass,
  73. 'entry' => $entryPath,
  74. 'loadedFromModel' => $loadedFromModel
  75. ];
  76. }
  77. }
  78. }
  79. // Always ensure neutral exists as fallback
  80. if (!isset($results['neutral'])) {
  81. // try to include NeutralTemplate
  82. $neutralFile = __DIR__ . '/NeutralTemplate.php';
  83. if (file_exists($neutralFile)) require_once $neutralFile;
  84. $results['neutral'] = [
  85. 'id' => 'neutral',
  86. 'name' => 'Neutre',
  87. 'description' => 'Template neutre',
  88. 'class' => class_exists('NeutralTemplate') ? 'NeutralTemplate' : null
  89. ];
  90. }
  91. self::$cache = $results;
  92. return $results;
  93. }
  94. /**
  95. * Retourne la classe du template demandé (ou NeutralTemplate)
  96. * @param string|null $id
  97. * @return string|null
  98. */
  99. public static function get(string $id = null): ?string
  100. {
  101. $list = self::listAvailable();
  102. if ($id && isset($list[$id]) && !empty($list[$id]['class'])) return $list[$id]['class'];
  103. return $list['neutral']['class'] ?? null;
  104. }
  105. }
  106. }