Dispatcher.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * MINZ - Copyright 2011 Marien Fressinaud
  5. * Sous licence AGPL3 <http://www.gnu.org/licenses/>
  6. */
  7. /**
  8. * The Dispatcher is in charge of initialising the Controller and execute the action as specified in the Request object.
  9. * It is a singleton.
  10. */
  11. final class Minz_Dispatcher {
  12. /**
  13. * Singleton
  14. */
  15. private static ?Minz_Dispatcher $instance = null;
  16. private static bool $needsReset;
  17. /** @var array<string,string> */
  18. private static array $registrations = [];
  19. private Minz_ActionController $controller;
  20. /**
  21. * Retrieves the Dispatcher instance
  22. */
  23. public static function getInstance(): Minz_Dispatcher {
  24. if (self::$instance === null) {
  25. self::$instance = new Minz_Dispatcher();
  26. }
  27. return self::$instance;
  28. }
  29. /**
  30. * Launches the controller specified in Request
  31. * Fills the Response body from the View
  32. * @throws Minz_Exception
  33. */
  34. public function run(): void {
  35. do {
  36. self::$needsReset = false;
  37. try {
  38. $this->createController(Minz_Request::controllerName());
  39. $this->controller->init();
  40. $this->controller->firstAction();
  41. if (!self::$needsReset) {
  42. $this->launchAction(
  43. Minz_Request::actionName()
  44. . 'Action'
  45. );
  46. }
  47. $this->controller->lastAction();
  48. if (!self::$needsReset) {
  49. $model = $this->controller->view();
  50. if ($model instanceof FreshRSS_View && $model->displaySlider) {
  51. FreshRSS_View::prependScript(Minz_Url::display('/scripts/extra.js?' . @filemtime(PUBLIC_PATH . '/scripts/extra.js')));
  52. }
  53. $this->controller->declareCspHeader();
  54. $this->controller->view()->build();
  55. }
  56. } catch (Minz_Exception $e) {
  57. throw $e;
  58. }
  59. } while (self::$needsReset);
  60. }
  61. /**
  62. * Informs the controller that it must restart because the request has been modified
  63. */
  64. public static function reset(): void {
  65. self::$needsReset = true;
  66. }
  67. /**
  68. * Instantiates the Controller
  69. * @param string $base_name the name of the controller to instantiate
  70. * @throws Minz_ControllerNotExistException the controller does not exist
  71. * @throws Minz_ControllerNotActionControllerException controller is not an instance of ActionController
  72. */
  73. private function createController(string $base_name): void {
  74. if (self::isRegistered($base_name)) {
  75. self::loadController($base_name);
  76. $controller_name = 'FreshExtension_' . $base_name . '_Controller';
  77. } else {
  78. $controller_name = 'FreshRSS_' . $base_name . '_Controller';
  79. }
  80. if (!class_exists($controller_name)) {
  81. throw new Minz_ControllerNotExistException(
  82. Minz_Exception::ERROR
  83. );
  84. }
  85. $controller = new $controller_name();
  86. if (!($controller instanceof Minz_ActionController)) {
  87. throw new Minz_ControllerNotActionControllerException(
  88. $controller_name,
  89. Minz_Exception::ERROR
  90. );
  91. }
  92. $this->controller = $controller;
  93. }
  94. /**
  95. * Launch the action on the dispatcher’s controller
  96. * @param string $action_name the name of the action
  97. * @throws Minz_ActionException if the action cannot be executed on the controller
  98. */
  99. private function launchAction(string $action_name): void {
  100. $call = [$this->controller, $action_name];
  101. if (!is_callable($call)) {
  102. throw new Minz_ActionException(
  103. get_class($this->controller),
  104. $action_name,
  105. Minz_Exception::ERROR
  106. );
  107. }
  108. if (Minz_ExtensionManager::callHook(Minz_HookType::ActionExecute, $this->controller) !== false) {
  109. call_user_func($call);
  110. }
  111. }
  112. /**
  113. * Register a controller file.
  114. *
  115. * @param string $base_name the base name of the controller (i.e. ./?c=<base_name>)
  116. * @param string $base_path the base path where we should look into to find info.
  117. */
  118. public static function registerController(string $base_name, string $base_path): void {
  119. if (!self::isRegistered($base_name)) {
  120. self::$registrations[$base_name] = $base_path;
  121. }
  122. }
  123. /**
  124. * Return if a controller is registered.
  125. *
  126. * @param string $base_name the base name of the controller.
  127. * @return bool true if the controller has been registered, false else.
  128. */
  129. public static function isRegistered(string $base_name): bool {
  130. return isset(self::$registrations[$base_name]);
  131. }
  132. /**
  133. * Load a controller file (include).
  134. *
  135. * @param string $base_name the base name of the controller.
  136. */
  137. private static function loadController(string $base_name): void {
  138. $base_path = self::$registrations[$base_name];
  139. $controller_filename = $base_path . '/Controllers/' . $base_name . 'Controller.php';
  140. include_once $controller_filename;
  141. }
  142. }