nur-sery/nur_src/v/base/AbstractPageContainer.php

323 lines
9.3 KiB
PHP
Raw Normal View History

2023-12-03 22:10:18 +04:00
<?php
namespace nur\v\base;
use nur\A;
use nur\b\coll\BaseArray;
use nur\b\coll\GenericArray;
use nur\b\ExitException;
use nur\co;
use nur\config;
use nur\func;
use nur\v\html5\Html5BasicErrorPage;
use nur\v\model\IChildComponent;
use nur\v\model\IComponent;
use nur\v\model\IErrorPage;
use nur\v\model\IPage;
use nur\v\model\IPageContainer;
use nur\v\model\IPlugin;
use nur\v\page;
use nur\v\prefix;
use nur\v\vo;
use Throwable;
abstract class AbstractPageContainer implements IPageContainer {
protected static function ensure_preparec(IComponent $c, bool $afterPrepare=false): bool {
if (!$c->didPrepare()) {
$c->beforePrepare();
$c->prepare();
if ($afterPrepare) $c->afterPrepare();
return true;
}
return false;
}
protected static function ensure_configc(IComponent $c, array &$config, bool $afterConfig=false): bool {
if (!$c->didConfig()) {
$c->beforeConfig($config);
$c->config($config);
if ($afterConfig) $c->afterConfig();
return true;
}
return false;
}
protected static function ensure_setupc(IComponent $c, bool $afterSetup=false): bool {
if (!$c->didSetup()) {
$c->beforeSetup();
$c->setup();
if ($afterSetup) $c->afterSetup();
return true;
}
return false;
}
protected static function ensure_teardownc(IComponent $c, bool $afterTeardown=false): bool {
if (!$c->didTeardown()) {
$c->beforeTeardown();
$c->teardown();
if ($afterTeardown) $c->afterTeardown();
return true;
}
return false;
}
protected static function printc(IComponent $c, ?array &$config): void {
self::ensure_preparec($c, true);
self::ensure_configc($c, $config, true);
self::ensure_setupc($c, true);
try {
if ($c->haveContent()) vo::write($c);
} finally {
self::ensure_teardownc($c, true);
}
}
#############################################################################
/** configurer les classes utilisées dans le cadre de ce container */
protected abstract function initToolkit(): void;
function __construct(?array $config=null) {
$this->initToolkit();
$this->config = [
"configure_options" => null,
"error_page_class" => null,
"self" => null,
"prefix" => prefix::compute(null),
"title" => null,
"css" => [],
"js" => [],
"plugins" => [],
];
$css = A::getdel($config, "css");
$js = A::getdel($config, "js");
$plugins = A::getdel($config, "plugins");
A::merge_nz($this->config, $config);
A::merge($this->config["css"], $css);
A::merge($this->config["js"], $js);
A::merge($this->config["plugins"], $plugins);
$this->params = new GenericArray();
$this->components = [];
}
/** @return array les options par défaut pour {@link config::configure()} */
protected function CONFIGURE_OPTIONS(): ?array {
return static::CONFIGURE_OPTIONS;
} const CONFIGURE_OPTIONS = null;
/** @return string la page utilisée pour afficher les erreurs */
protected function ERROR_PAGE_CLASS(): string {
return static::ERROR_PAGE_CLASS;
} const ERROR_PAGE_CLASS = Html5BasicErrorPage::class;
/**
* @return string chemin du script correspondant à cette page, depuis la racine
* de l'application e.g. index.php, ou null s'il faut utiliser la valeur par
* défaut
*/
protected function SELF(): ?string {
return static::SELF;
} const SELF = null;
/** @return string titre de la page */
protected function TITLE(): ?string {
return static::TITLE;
} const TITLE = null;
/** @return ?string|array liste de feuilles CSS à charger dans la page */
protected function CSS() {
return static::CSS;
} const CSS = null;
/** @return ?string|array liste de scripts à charger dans la page */
protected function JS() {
return static::JS;
} const JS = null;
/** @return ?string|array liste de plugins à ajouter à cette page */
protected function PLUGINS() {
return static::PLUGINS;
} const PLUGINS = null;
/** @var array */
protected $config;
protected function initConfig(): void {
A::replace_z($this->config, "configure_options", $this->CONFIGURE_OPTIONS());
A::replace_z($this->config, "error_page_class", $this->ERROR_PAGE_CLASS());
$self = $this->SELF();
if ($self !== null) {
A::merge($this->config, [
"self" => $self,
"prefix" => prefix::compute($self),
]);
}
A::replace_z($this->config, "title", $this->TITLE());
A::merge($this->config["css"], $this->CSS());
A::merge($this->config["js"], $this->JS());
A::merge($this->config["plugins"], $this->PLUGINS());
}
function getConfig(): array { return $this->config; }
function getErrorPage(): IErrorPage {
# NB: cette fonction peut être appelée AVANT initConfig()
$error_page_class = $this->config["error_page_class"];
if ($error_page_class === null) $error_page_class = $this->ERROR_PAGE_CLASS();
if ($error_page_class === null) $error_page_class = Html5BasicErrorPage::class;
$error_page = new $error_page_class();
$error_page->initContainer($this);
return $error_page;
}
function getSelf(): ?string { return $this->config["self"]; }
function getSelfRelativePrefix(): string { return $this->config["prefix"]; }
function getTitle(): string { return $this->config["title"]; }
function getCssUrls(): array { return $this->config["css"]; }
function getJsUrls(): array { return $this->config["js"]; }
function getPlugins(): array { return $this->config["plugins"]; }
/** @var BaseArray */
protected $params;
function getParams(): BaseArray {
return $this->params;
}
function haveError(): bool {
return boolval($this->params["page_error"]);
}
function setError(?string $message, ?Throwable $exception=null): void {
$this->params["page_error"] = [
"message" => $message,
"exception" => $exception,
];
}
function getError(): ?array {
return $this->params["page_error"];
}
# null = init, 0 = prepare, 1 = config, 2 = setup, 3 = print, 4 = teardown
const INIT_PHASE = null;
const PREPARE_PHASE = 0;
const CONFIG_PHASE = 1;
const SETUP_PHASE = 2;
const PRINT_PHASE = 3;
const TEARDOWN_PHASE = 4;
protected $phase;
protected function ensure_phasec(IComponent $c) {
$phase = $this->phase;
if ($phase !== self::INIT_PHASE) {
if ($phase >= self::PREPARE_PHASE) self::ensure_preparec($c);
if ($phase >= self::CONFIG_PHASE) self::ensure_configc($c, $this->config);
if ($phase >= self::SETUP_PHASE) self::ensure_setupc($c);
if ($phase >= self::TEARDOWN_PHASE) self::ensure_teardownc($c);
}
}
/** @var array composants enregistrés dans ce container, indexés par nom */
protected $components;
function addPlugin($plugin, ?string $name=null): IPlugin {
if (is_string($plugin)) $plugin = new $plugin();
elseif (is_array($plugin)) $plugin = func::cons(...$plugin);
if ($plugin instanceof IChildComponent) $plugin->initContainer($this);
if ($plugin instanceof IComponent) $this->ensure_phasec($plugin);
A::set($this->components, $name, $plugin);
return $plugin;
}
/** @var IPage composant de page à afficher dans ce container */
protected $page;
function setPage(IPage $page): void {
if ($page instanceof IChildComponent) $page->initContainer($this);
$this->page = $page;
$this->haveOutput = false;
}
/** @var bool ce container a-t-il déjà commencé à afficher du contenu? */
protected $haveOutput;
function haveOutput(): bool {
return $this->haveOutput;
}
function print(): void {
$page = $this->page;
page::set_current_page($page);
try {
$this->phase = self::PREPARE_PHASE;
if (self::ensure_preparec($page)) {
$this->overridePrepare($page);
$page->afterPrepare();
}
$this->phase = self::CONFIG_PHASE;
$this->initConfig();
if (self::ensure_configc($page, $this->config)) {
$this->overrideConfig($page);
$page->afterConfig();
}
config::configure($this->config["configure_options"]);
$this->phase = self::SETUP_PHASE;
if (self::ensure_setupc($page)) {
$this->overrideSetup($page);
$page->afterSetup();
}
$this->phase = self::PRINT_PHASE;
$this->overridePrint($page);
} catch (Throwable $e) {
if ($e instanceof ExitException && !$e->isError()) {
# NOP
} else {
$this->setError(null, $e);
}
} finally {
if ($this->haveError()) {
$errorPage = $this->getErrorPage();
if ($this->haveOutput()) {
# n'afficher que l'erreur si on a déjà du contenu
$errorPage->printError();
} else {
# sinon afficher la page entière
$errorPage->print();
}
}
if ($page->didSetup()) {
$this->phase = self::TEARDOWN_PHASE;
if (self::ensure_teardownc($page)) {
$this->overrideTeardown($page);
$page->afterTeardown();
}
}
}
}
protected function overridePrepare(IPage $page): void {
}
protected function overrideConfig(IPage $page): void {
}
protected function overrideSetup(IPage $page): void {
}
protected function overridePrint(IPage $page): void {
if ($page->haveContent()) {
$this->haveOutput = true;
co::_print([$page]);
}
}
protected function overrideTeardown(IPage $page): void {
}
}