diff --git a/src/app/web/Application.php b/src/app/web/Application.php new file mode 100644 index 0000000..fd6a255 --- /dev/null +++ b/src/app/web/Application.php @@ -0,0 +1,236 @@ +render($page ?? static::PAGE); + } catch (ExitError $e) { + if ($e->haveUserMessage()) log::error($e->getUserMessage()); + exit($e->getCode()); + } catch (Exception $e) { + log::error($e); + exit(app::EC_UNEXPECTED); + } + } + + protected static function _initialize_app(): void { + app::init(static::class); + app::set_fact(app::FACT_WEB_APP); + + $projcode = static::PROJCODE ?? "application"; + $log = new LogMessenger([ + "output" => "/tmp/$projcode.out", + "min_level" => msg::DEBUG, + ]); + con::set_messenger($log); + msg::set_messenger($log); + } + + /** + * sortir de l'application avec un code d'erreur, qui est 0 par défaut (i.e + * pas d'erreur) + * + * équivalent à lancer l'exception {@link ExitError} + */ + protected static final function exit(int $exitcode=0, $message=null) { + throw new ExitError($exitcode, $message); + } + + /** + * sortir de l'application avec un code d'erreur, qui vaut 1 par défaut (i.e + * une erreur s'est produite) + * + * équivalent à lancer l'exception {@link ExitError} + */ + protected static final function die($message=null, int $exitcode=1) { + throw new ExitError($exitcode, $message); + } + + protected RouteManager $routeManager; + + function getRouteManager(): RouteManager { + return $this->routeManager; + } + + protected function configureRoutes(): void { + $this->routeManager->addRoute(...static::ROUTES); + } + + protected function configureLogs(): void { + $log = log::set_messenger(new LogMessenger([ + "output" => app::get()->getLogfile(), + "min_level" => msg::MINOR, + ])); + msg::set_messenger($log, true); + } + + protected function configureNoSession(): void { + config::configure(config::CONFIGURE_NO_SESSION); + } + + protected function configureSession(): void { + config::configure(); + } + + protected function resolvePage($page): IPage { + if ($page instanceof IPage) return $page; + $page ??= $this->routeManager->getPage(); + if (is_string($page)) $page = new $page(); + elseif (is_array($page)) $page = func::with($page)->invoke(); + else throw exceptions::invalid_type($page, "page", ["string", "array"]); + return $page; + } + + function ensurePhase(IPage $page, int $phase, bool $after=false, &$output=null) { + if (!$page->didComponentPhase($phase)) { + if ($page->beforeComponentPhase($phase)) { + page::set_component_phase($phase); + $output = $page->doComponentPhase($phase); + if ($after) $page->afterComponentPhase($phase); + return true; + } + } + return false; + } + + protected ?array $renderers = null; + + protected function getRenderer(IPage $page) { + $class = $page->RENDERER_CLASS() ?? Html5PageRenderer::class; + return $this->renderers[$class] ??= new $class(); + } + + function render($component) { + config::configure(config::CONFIGURE_INITIAL_ONLY); + $this->routeManager = new RouteManager(); + $this->configureRoutes(); + $this->configureLogs(); + + $components = []; + $component = $this->resolvePage($component); + while (true) { + page::set_component($component); + array_unshift($components, $component); + if (!$this->ensurePhase($component, page::PHASE_PREPARE)) break; + $component->afterComponentPhase(page::PHASE_PREPARE); + + if ($component->USE_SESSION()) $this->configureSession(); + else $this->configureNoSession(); + if (!$this->ensurePhase($component, page::PHASE_CONFIGURE)) break; + $component->afterComponentPhase(page::PHASE_CONFIGURE); + + if (!$this->ensurePhase($component, page::PHASE_SETUP, false, $output)) break; + $component->afterComponentPhase(page::PHASE_SETUP); + page::set_component_phase(page::PHASE_PRINT); + if ($output instanceof IComponent) { + # composant autonome. recommencer la phase de préparation + } elseif (is_callable($output)) { + # générateur de contenu + $output(); + break; + } elseif ($output === false) { + # retour en l'état (contenu déjà généré dans setup) + break; + } elseif ($output !== null) { + # contenu json ou texte + if (is_iterable($output)) $renderer = new JsonRenderer(); + else $renderer = new TextRenderer(); + page::set_renderer($renderer); + $renderer->render($output); + break; + } else { + # afficher la page normalement + $renderer = $this->getRenderer($component); + page::set_renderer($renderer); + $renderer->render($component); + break; + } + } + foreach ($components as $component) { + if ($component->didComponentPhase(page::PHASE_SETUP)) { + $this->ensurePhase($component, page::PHASE_TEARDOWN, true); + } + } + } +} diff --git a/src/web/base/RouteManager.php b/src/app/web/RouteManager.php similarity index 99% rename from src/web/base/RouteManager.php rename to src/app/web/RouteManager.php index 5981668..85391bb 100644 --- a/src/web/base/RouteManager.php +++ b/src/app/web/RouteManager.php @@ -1,5 +1,5 @@ page = $page; + $this->printStartHtml(); + $this->printStartHead(); + $this->printCssLinks(); + $this->printCss(); + $this->printJsLinks(); + $this->printJs(); + $this->printScript(); + $this->printHeadTitle(); + $this->printEndHead(); + $this->printStartBody(); + $this->printEndBody(); + $this->printEndHtml(); + } else { + throw exceptions::invalid_type($page, "page", IPage::class); + } + } + + protected IPage $page; + + function printStartHtml(): void { + ?> + + +page->getSelfRelativePrefix(); + ?> +
+ + + + +page->getSelfRelativePrefix(); + foreach ($this->cssUrls as $url) { + $url = prefix::add($url, $prefix); + ?> + +getPlugins() as $plugin) { + $plugin->printCss(); + } + } + + function printJsLinks(): void { + $prefix = $this->page->getSelfRelativePrefix(); + foreach ($this->jsUrls as $url) { + $url = prefix::add($url, $prefix); + ?> + +getPlugins() as $plugin) { + $plugin->printJs(); + } + } + + protected function beforeCapture(): void { + ob_start(null, 0, PHP_OUTPUT_HANDLER_STDFLAGS ^ PHP_OUTPUT_HANDLER_FLUSHABLE); + } + + private static function strip_lines(array &$lines): void { + while (count($lines) > 0 && !$lines[0]) { + # enlever les lignes vides au début + array_shift($lines); + } + while (($count = count($lines)) > 0 && !$lines[$count - 1]) { + # enlever les lignes vides à la fin + array_pop($lines); + } + } + + const RE_START_SCRIPT = '/^\s* +resolvePluginsScripts(); + $this->printMergedScripts(); + } + + function printHeadTitle(): void { + $title = c::to_string($this->page->TITLE()); + echo "