Dispatcher.php 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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 exectue 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. // @phpstan-ignore booleanNot.alwaysTrue
  42. if (!self::$needsReset) {
  43. $this->launchAction(
  44. Minz_Request::actionName()
  45. . 'Action'
  46. );
  47. }
  48. $this->controller->lastAction();
  49. // @phpstan-ignore booleanNot.alwaysTrue
  50. if (!self::$needsReset) {
  51. $this->controller->declareCspHeader();
  52. $this->controller->view()->build();
  53. }
  54. } catch (Minz_Exception $e) {
  55. throw $e;
  56. }
  57. // @phpstan-ignore doWhile.alwaysFalse
  58. } while (self::$needsReset);
  59. }
  60. /**
  61. * Informs the controller that it must restart because the request has been modified
  62. */
  63. public static function reset(): void {
  64. self::$needsReset = true;
  65. }
  66. /**
  67. * Instantiates the Controller
  68. * @param string $base_name the name of the controller to instantiate
  69. * @throws Minz_ControllerNotExistException the controller does not exist
  70. * @throws Minz_ControllerNotActionControllerException controller is not an instance of ActionController
  71. */
  72. private function createController(string $base_name): void {
  73. if (self::isRegistered($base_name)) {
  74. self::loadController($base_name);
  75. $controller_name = 'FreshExtension_' . $base_name . '_Controller';
  76. } else {
  77. $controller_name = 'FreshRSS_' . $base_name . '_Controller';
  78. }
  79. if (!class_exists($controller_name)) {
  80. throw new Minz_ControllerNotExistException(
  81. Minz_Exception::ERROR
  82. );
  83. }
  84. $controller = new $controller_name();
  85. if (!($controller instanceof Minz_ActionController)) {
  86. throw new Minz_ControllerNotActionControllerException(
  87. $controller_name,
  88. Minz_Exception::ERROR
  89. );
  90. }
  91. $this->controller = $controller;
  92. }
  93. /**
  94. * Launch the action on the dispatcher’s controller
  95. * @param string $action_name the name of the action
  96. * @throws Minz_ActionException if the action cannot be executed on the controller
  97. */
  98. private function launchAction(string $action_name): void {
  99. $call = [$this->controller, $action_name];
  100. if (!is_callable($call)) {
  101. throw new Minz_ActionException(
  102. get_class($this->controller),
  103. $action_name,
  104. Minz_Exception::ERROR
  105. );
  106. }
  107. call_user_func($call);
  108. }
  109. /**
  110. * Register a controller file.
  111. *
  112. * @param string $base_name the base name of the controller (i.e. ./?c=<base_name>)
  113. * @param string $base_path the base path where we should look into to find info.
  114. */
  115. public static function registerController(string $base_name, string $base_path): void {
  116. if (!self::isRegistered($base_name)) {
  117. self::$registrations[$base_name] = $base_path;
  118. }
  119. }
  120. /**
  121. * Return if a controller is registered.
  122. *
  123. * @param string $base_name the base name of the controller.
  124. * @return bool true if the controller has been registered, false else.
  125. */
  126. public static function isRegistered(string $base_name): bool {
  127. return isset(self::$registrations[$base_name]);
  128. }
  129. /**
  130. * Load a controller file (include).
  131. *
  132. * @param string $base_name the base name of the controller.
  133. */
  134. private static function loadController(string $base_name): void {
  135. $base_path = self::$registrations[$base_name];
  136. $controller_filename = $base_path . '/Controllers/' . $base_name . 'Controller.php';
  137. include_once $controller_filename;
  138. }
  139. }