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;
|
2024-07-16 01:54:30 +04:00
|
|
|
use nur\b\ExitError;
|
2023-12-03 22:10:18 +04:00
|
|
|
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) {
|
2024-07-16 01:54:30 +04:00
|
|
|
if ($e instanceof ExitError && !$e->isError()) {
|
2023-12-03 22:10:18 +04:00
|
|
|
# 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 {
|
|
|
|
}
|
|
|
|
}
|