application web
This commit is contained in:
parent
0f6a76e701
commit
944daa8ea4
236
src/app/web/Application.php
Normal file
236
src/app/web/Application.php
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\app\web;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use nulib\app\app;
|
||||||
|
use nulib\app\config;
|
||||||
|
use nulib\exceptions;
|
||||||
|
use nulib\ExitError;
|
||||||
|
use nulib\output\con;
|
||||||
|
use nulib\output\log;
|
||||||
|
use nulib\output\msg;
|
||||||
|
use nulib\output\std\LogMessenger;
|
||||||
|
use nulib\php\func;
|
||||||
|
use nulib\web\base\Html5PageRenderer;
|
||||||
|
use nulib\web\base\JsonRenderer;
|
||||||
|
use nulib\web\base\TextRenderer;
|
||||||
|
use nulib\web\model\IComponent;
|
||||||
|
use nulib\web\model\IPage;
|
||||||
|
use nulib\web\page;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Application: application de base
|
||||||
|
*/
|
||||||
|
abstract class Application {
|
||||||
|
/** @var string répertoire du projet (celui qui contient composer.json */
|
||||||
|
const PROJDIR = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array répertoires vendor exprimés relativement à PROJDIR
|
||||||
|
*
|
||||||
|
* les clés suivantes doivent être présentes dans le tableau:
|
||||||
|
* - autoload (chemin vers vendor/autoload.php)
|
||||||
|
* - bindir (chemin vers vendor/bin)
|
||||||
|
*/
|
||||||
|
const VENDOR = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string code du projet, utilisé pour dériver le noms de certains des
|
||||||
|
* paramètres extraits de l'environnement, e.g MY_APP_DATADIR si le projet a
|
||||||
|
* pour code my-app
|
||||||
|
*
|
||||||
|
* si non définie, cette valeur est calculée automatiquement à partir de
|
||||||
|
* self::PROJDIR sans le suffixe "-app"
|
||||||
|
*/
|
||||||
|
const PROJCODE = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string|null identifiant d'un groupe auquel l'application appartient.
|
||||||
|
* les applications du même groupe enregistrent leur fichiers de controle au
|
||||||
|
* même endroit $VARDIR/$APPGROUP
|
||||||
|
*/
|
||||||
|
const APPGROUP = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string code de l'application, utilisé pour inférer le nom de certains
|
||||||
|
* fichiers spécifiques à l'application.
|
||||||
|
*
|
||||||
|
* si non définie, cette valeur est calculée automatiquement à partir de
|
||||||
|
* static::class
|
||||||
|
*/
|
||||||
|
const NAME = null;
|
||||||
|
|
||||||
|
/** @var string description courte de l'application */
|
||||||
|
const TITLE = null;
|
||||||
|
|
||||||
|
const DATADIR = null;
|
||||||
|
const ETCDIR = null;
|
||||||
|
const VARDIR = null;
|
||||||
|
const CACHEDIR = null;
|
||||||
|
const LOGDIR = null;
|
||||||
|
|
||||||
|
const MODE_EXACT = RouteManager::MODE_EXACT;
|
||||||
|
const MODE_PREFIX = RouteManager::MODE_PREFIX;
|
||||||
|
const MODE_PACKAGE = RouteManager::MODE_PACKAGE;
|
||||||
|
const MODE_PACKAGE2 = RouteManager::MODE_PACKAGE2;
|
||||||
|
|
||||||
|
const ROUTES = [
|
||||||
|
];
|
||||||
|
|
||||||
|
const PAGE = null;
|
||||||
|
|
||||||
|
static function run($page=null, ?Application $app=null): void {
|
||||||
|
try {
|
||||||
|
static::_initialize_app();
|
||||||
|
$app ??= new static();
|
||||||
|
page::set_app($app);
|
||||||
|
$app->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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nulib\web\base;
|
namespace nulib\app\web;
|
||||||
|
|
||||||
use nulib\cl;
|
use nulib\cl;
|
||||||
use nulib\cv;
|
use nulib\cv;
|
||||||
17
src/web/base/AbstractComponent.php
Normal file
17
src/web/base/AbstractComponent.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\web\base;
|
||||||
|
|
||||||
|
use nulib\web\model\IComponent;
|
||||||
|
|
||||||
|
abstract class AbstractComponent implements IComponent {
|
||||||
|
use TComponentPhase;
|
||||||
|
|
||||||
|
const COMPONENT_CAN_PREPARE = false;
|
||||||
|
const COMPONENT_CAN_CONFIGURE = false;
|
||||||
|
const COMPONENT_CAN_SETUP = false;
|
||||||
|
const COMPONENT_CAN_TEARDOWN = false;
|
||||||
|
|
||||||
|
function RENDERER_CLASS(): ?string {
|
||||||
|
return static::RENDERER_CLASS;
|
||||||
|
} const RENDERER_CLASS = null;
|
||||||
|
}
|
||||||
15
src/web/base/AbstractPage.php
Normal file
15
src/web/base/AbstractPage.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\web\base;
|
||||||
|
|
||||||
|
use nulib\web\model\IPage;
|
||||||
|
|
||||||
|
abstract class AbstractPage extends AbstractComponent implements IPage {
|
||||||
|
function USE_SESSION(): bool {
|
||||||
|
return static::USE_SESSION;
|
||||||
|
} const USE_SESSION = false;
|
||||||
|
|
||||||
|
/** faut-il fermer automatiquement la session avant l'affichage? */
|
||||||
|
protected function AUTOCLOSE_SESSION(): bool {
|
||||||
|
return static::AUTOCLOSE_SESSION;
|
||||||
|
} const AUTOCLOSE_SESSION = true;
|
||||||
|
}
|
||||||
@ -1,10 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nulib\web\base;
|
namespace nulib\web\base;
|
||||||
|
|
||||||
use nulib\web\model\IPage;
|
class BasicPage extends AbstractPage {
|
||||||
|
|
||||||
class BasicPage implements IPage {
|
|
||||||
use TRenderer;
|
|
||||||
|
|
||||||
const RENDERER = Html5Renderer::class;
|
|
||||||
}
|
}
|
||||||
|
|||||||
246
src/web/base/Html5PageRenderer.php
Normal file
246
src/web/base/Html5PageRenderer.php
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\web\base;
|
||||||
|
|
||||||
|
use nulib\exceptions;
|
||||||
|
use nulib\php\content\c;
|
||||||
|
use nulib\str;
|
||||||
|
use nulib\web\ly;
|
||||||
|
use nulib\web\model\IPage;
|
||||||
|
use nulib\web\model\IRenderer;
|
||||||
|
use nulib\web\page;
|
||||||
|
|
||||||
|
class Html5PageRenderer implements IRenderer {
|
||||||
|
function render($page): void {
|
||||||
|
if ($page instanceof IPage) {
|
||||||
|
page::set_html_output();
|
||||||
|
$this->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 {
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr" xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
function printStartHead(): void {
|
||||||
|
$prefix = $this->page->getSelfRelativePrefix();
|
||||||
|
?>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link href="<?=$prefix?>nur-base/base.css" rel="stylesheet"/>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
function printCssLinks(): void {
|
||||||
|
$prefix = $this->page->getSelfRelativePrefix();
|
||||||
|
foreach ($this->cssUrls as $url) {
|
||||||
|
$url = prefix::add($url, $prefix);
|
||||||
|
?>
|
||||||
|
<link href="<?=$url?>" rel="stylesheet"/>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function printCss(): void {
|
||||||
|
foreach ($this->getPlugins() as $plugin) {
|
||||||
|
$plugin->printCss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function printJsLinks(): void {
|
||||||
|
$prefix = $this->page->getSelfRelativePrefix();
|
||||||
|
foreach ($this->jsUrls as $url) {
|
||||||
|
$url = prefix::add($url, $prefix);
|
||||||
|
?>
|
||||||
|
<script src="<?=$url?>" type="text/javascript"></script>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function printJs(): void {
|
||||||
|
foreach ($this->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*<script\s+type="(?:text\/)?javascript">\s*(.*)/';
|
||||||
|
const RE_END_SCRIPT = '/(.*?)\s*<\/script>\s*$/';
|
||||||
|
|
||||||
|
private static function unwrap_script(array &$lines): void {
|
||||||
|
$count = count($lines);
|
||||||
|
if ($count >= 2) {
|
||||||
|
$first = $lines[0];
|
||||||
|
$last = $lines[$count - 1];
|
||||||
|
if (preg_match(self::RE_START_SCRIPT, $first)
|
||||||
|
&& preg_match(self::RE_END_SCRIPT, $last)) {
|
||||||
|
$last = preg_replace(self::RE_END_SCRIPT, '$1', $last);
|
||||||
|
if ($last) $lines[$count - 1] = $last;
|
||||||
|
else $lines = array_slice($lines, 0, $count - 1);
|
||||||
|
$first = preg_replace(self::RE_START_SCRIPT, '$1', $first);
|
||||||
|
if ($first) $lines[0] = $first;
|
||||||
|
else $lines = array_slice($lines, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected $scripts = [];
|
||||||
|
protected function captureScript(): void {
|
||||||
|
$lines = trim(ob_get_clean());
|
||||||
|
if ($lines) {
|
||||||
|
$lines = str::split_nl($lines);
|
||||||
|
self::strip_lines($lines);
|
||||||
|
self::unwrap_script($lines);
|
||||||
|
$this->scripts[] = implode("\n", $lines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const RE_START_JQUERY = '/^\s*jQuery(?:.noConflict\(\))?\(function\(\$\) {\s*(.*)/';
|
||||||
|
const RE_END_JQUERY = '/(.*?)\s*}\);\s*$/';
|
||||||
|
|
||||||
|
private static function unwrap_jquery(array &$lines): void {
|
||||||
|
$count = count($lines);
|
||||||
|
if ($count >= 2) {
|
||||||
|
$first = $lines[0];
|
||||||
|
$last = $lines[$count - 1];
|
||||||
|
if (preg_match(self::RE_START_JQUERY, $first)
|
||||||
|
&& preg_match(self::RE_END_JQUERY, $last)) {
|
||||||
|
$last = preg_replace(self::RE_END_JQUERY, '$1', $last);
|
||||||
|
if ($last) $lines[$count - 1] = $last;
|
||||||
|
else $lines = array_slice($lines, 0, $count - 1);
|
||||||
|
$first = preg_replace(self::RE_START_JQUERY, '$1', $first);
|
||||||
|
if ($first) $lines[0] = $first;
|
||||||
|
else $lines = array_slice($lines, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected $jqueries = [];
|
||||||
|
protected function captureJquery(): void {
|
||||||
|
$lines = trim(ob_get_clean());
|
||||||
|
if ($lines) {
|
||||||
|
$lines = str::split_nl($lines);
|
||||||
|
self::strip_lines($lines);
|
||||||
|
self::unwrap_script($lines);
|
||||||
|
self::unwrap_jquery($lines);
|
||||||
|
$this->jqueries[] = implode("\n", $lines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function resolvePluginsScripts(): void {
|
||||||
|
foreach ($this->getPlugins() as $plugin) {
|
||||||
|
if ($plugin->haveScript()) {
|
||||||
|
$this->beforeCapture();
|
||||||
|
$plugin->printScript();
|
||||||
|
$this->captureScript();
|
||||||
|
}
|
||||||
|
if ($plugin->haveJquery()) {
|
||||||
|
$this->beforeCapture();
|
||||||
|
$plugin->printJquery();
|
||||||
|
$this->captureJquery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function printMergedScripts(): void {
|
||||||
|
$scripts = $this->scripts;
|
||||||
|
$jqueries = $this->jqueries;
|
||||||
|
if ($scripts || $jqueries) {
|
||||||
|
?>
|
||||||
|
<script type="text/javascript">
|
||||||
|
<?php
|
||||||
|
foreach ($scripts as $script) {
|
||||||
|
echo "$script\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($jqueries) {
|
||||||
|
?>
|
||||||
|
jQuery.noConflict()(function($) {
|
||||||
|
<?php
|
||||||
|
foreach ($jqueries as $jquery) {
|
||||||
|
echo "$jquery\n";
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
});
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
if ($scripts || $jqueries) {
|
||||||
|
?>
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function printScript(): void {
|
||||||
|
$this->resolvePluginsScripts();
|
||||||
|
$this->printMergedScripts();
|
||||||
|
}
|
||||||
|
|
||||||
|
function printHeadTitle(): void {
|
||||||
|
$title = c::to_string($this->page->TITLE());
|
||||||
|
echo "<title>$title</title>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
function printEndHead(): void {
|
||||||
|
?>
|
||||||
|
</head>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
function printStartBody(): void {
|
||||||
|
?>
|
||||||
|
<body>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
function printContent(): void {
|
||||||
|
c::write($this->page);
|
||||||
|
ly::end();
|
||||||
|
}
|
||||||
|
|
||||||
|
function printEndBody(): void {
|
||||||
|
?>
|
||||||
|
</body>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
function printEndHtml(): void {
|
||||||
|
?>
|
||||||
|
</html>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace nulib\web\base;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use nulib\output\log;
|
|
||||||
use nulib\php\func;
|
|
||||||
use nulib\web\model\IPage;
|
|
||||||
use nulib\web\model\IRenderer;
|
|
||||||
use nulib\web\page;
|
|
||||||
use nulib\web\route;
|
|
||||||
|
|
||||||
class Html5Renderer implements IRenderer {
|
|
||||||
function render(IPage $page): void {
|
|
||||||
page::set_current($page);
|
|
||||||
try {
|
|
||||||
|
|
||||||
} catch (Exception $e) {
|
|
||||||
if (page::is_error()) {
|
|
||||||
log::error($e);
|
|
||||||
} else {
|
|
||||||
page::set_error();
|
|
||||||
$this->render(func::with(route::get_error($e))->invoke());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
16
src/web/base/HtmlRenderer.php
Normal file
16
src/web/base/HtmlRenderer.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\web\base;
|
||||||
|
|
||||||
|
use nulib\web\model\IComponent;
|
||||||
|
use nulib\web\model\IRenderer;
|
||||||
|
|
||||||
|
class HtmlRenderer implements IRenderer {
|
||||||
|
function render($data): void {
|
||||||
|
header("Content-Type: text/html; charset=utf-8");
|
||||||
|
if ($data instanceof IComponent) {
|
||||||
|
$data->print();
|
||||||
|
} else {
|
||||||
|
echo $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/web/base/JsonRenderer.php
Normal file
23
src/web/base/JsonRenderer.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\web\base;
|
||||||
|
|
||||||
|
use nulib\web\model\IRenderer;
|
||||||
|
use nur\json;
|
||||||
|
|
||||||
|
class JsonRenderer implements IRenderer {
|
||||||
|
function render($data): void {
|
||||||
|
header("Content-Type: application/json");
|
||||||
|
if (is_iterable($data)) {
|
||||||
|
echo "[";
|
||||||
|
$sep = "";
|
||||||
|
foreach ($data as $datum) {
|
||||||
|
$line = json::encode($datum);
|
||||||
|
echo "$sep$line\n";
|
||||||
|
$sep = ",";
|
||||||
|
}
|
||||||
|
echo "]";
|
||||||
|
} else {
|
||||||
|
echo json::encode($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/web/base/TComponentPhase.php
Normal file
37
src/web/base/TComponentPhase.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\web\base;
|
||||||
|
|
||||||
|
use nulib\web\page;
|
||||||
|
|
||||||
|
trait TComponentPhase {
|
||||||
|
protected int $currentComponentPhase;
|
||||||
|
|
||||||
|
function beforeComponentPhase(int $phase): bool {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function doComponentPhase(int $phase) {
|
||||||
|
$this->currentComponentPhase = $phase;
|
||||||
|
switch ($phase) {
|
||||||
|
case page::PHASE_PREPARE:
|
||||||
|
if (static::COMPONENT_CAN_PREPARE) $this->prepare();
|
||||||
|
break;
|
||||||
|
case page::PHASE_CONFIGURE:
|
||||||
|
if (static::COMPONENT_CAN_CONFIGURE) $this->configure();
|
||||||
|
break;
|
||||||
|
case page::PHASE_SETUP:
|
||||||
|
if (static::COMPONENT_CAN_SETUP) return $this->setup();
|
||||||
|
case page::PHASE_TEARDOWN:
|
||||||
|
if (static::COMPONENT_CAN_TEARDOWN) $this->teardown();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function afterComponentPhase(int $phase): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
function didComponentPhase(int $phase): bool {
|
||||||
|
return $phase >= $this->currentComponentPhase;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,11 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace nulib\web\base;
|
|
||||||
|
|
||||||
use nulib\web\model\IRenderer;
|
|
||||||
|
|
||||||
trait TRenderer {
|
|
||||||
function RENDERER(): IRenderer {
|
|
||||||
$class = static::RENDERER ?? Html5Renderer::class;
|
|
||||||
return new $class();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
11
src/web/base/TextRenderer.php
Normal file
11
src/web/base/TextRenderer.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\web\base;
|
||||||
|
|
||||||
|
use nulib\web\model\IRenderer;
|
||||||
|
|
||||||
|
class TextRenderer implements IRenderer {
|
||||||
|
function render($data): void {
|
||||||
|
header("Content-Type: text/plain; charset=utf-8");
|
||||||
|
echo $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/web/ly.php
Normal file
7
src/web/ly.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\web;
|
||||||
|
|
||||||
|
class ly {
|
||||||
|
static function end(): void {
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/web/model/IComponent.php
Normal file
12
src/web/model/IComponent.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\web\model;
|
||||||
|
|
||||||
|
interface IComponent {
|
||||||
|
/** retourner la classe de renderer à utiliser pour afficher ce composant */
|
||||||
|
function RENDERER_CLASS(): ?string;
|
||||||
|
|
||||||
|
function beforeComponentPhase(int $phase): bool;
|
||||||
|
function doComponentPhase(int $phase);
|
||||||
|
function afterComponentPhase(int $phase): void;
|
||||||
|
function didComponentPhase(int $phase): bool;
|
||||||
|
}
|
||||||
@ -1,6 +1,29 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nulib\web\model;
|
namespace nulib\web\model;
|
||||||
|
|
||||||
interface IPage {
|
interface IPage extends IComponent {
|
||||||
function RENDERER(): IRenderer;
|
/** indiquer si cette page a besoin d'avoir une session valide */
|
||||||
|
function USE_SESSION(): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retourner le chemin vers le *script* courant par rapport à la racine de
|
||||||
|
* l'application, e.g 'index.php' s'il est différent de la valeur par défaut
|
||||||
|
* basename($_SERVER["SCRIPT_NAME"]), ou null pour utiliser la valeur par
|
||||||
|
* défaut
|
||||||
|
*
|
||||||
|
* celà permet de calculer les chemins relatifs aux resources, même si
|
||||||
|
* l'application n'est pas servie à la racine ou si on utilise path_info
|
||||||
|
*/
|
||||||
|
function SELF(): ?string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retourner le titre de la page, ou null pour utiliser la valeur par défaut
|
||||||
|
*/
|
||||||
|
function TITLE(): ?string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retourner le préfixe à rajouter aux chemins relatifs exprimés depuis la
|
||||||
|
* racine pour les atteindre depuis la page courante
|
||||||
|
*/
|
||||||
|
function getSelfRelativePrefix(): string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,5 +2,5 @@
|
|||||||
namespace nulib\web\model;
|
namespace nulib\web\model;
|
||||||
|
|
||||||
interface IRenderer {
|
interface IRenderer {
|
||||||
function render(IPage $page): void;
|
function render($data): void;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,26 +1,65 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nulib\web;
|
namespace nulib\web;
|
||||||
|
|
||||||
use nulib\app\app;
|
use nulib\app\web\Application;
|
||||||
use nulib\app\config;
|
use nulib\web\model\IComponent;
|
||||||
use nulib\exceptions;
|
use nulib\web\model\IRenderer;
|
||||||
use nulib\php\func;
|
|
||||||
use nulib\web\model\IPage;
|
|
||||||
|
|
||||||
class page {
|
class page {
|
||||||
static function render($page=null): void {
|
protected static ?Application $app = null;
|
||||||
app::set_fact(app::FACT_WEB_APP);
|
|
||||||
config::configure(config::CONFIGURE_INITIAL_ONLY);
|
static function set_app(Application $app) {
|
||||||
if (!($page instanceof IPage)) {
|
self::$app = $app;
|
||||||
if ($page === null) {
|
}
|
||||||
config::configure(config::CONFIGURE_ROUTES_ONLY);
|
|
||||||
$page = route::get_page();
|
static function get_app(): ?Application {
|
||||||
}
|
return self::$app;
|
||||||
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"]);
|
protected static ?IComponent $component = null;
|
||||||
}
|
|
||||||
/** @var IPage $page */
|
static function set_component(IComponent $component) {
|
||||||
$page->RENDERER()->render($page);
|
self::$component = $component;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function get_component(): ?IComponent {
|
||||||
|
return self::$component;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PHASE_NONE = 0;
|
||||||
|
const PHASE_PREPARE = 1;
|
||||||
|
const PHASE_CONFIGURE = 2;
|
||||||
|
const PHASE_SETUP = 3;
|
||||||
|
const PHASE_PRINT = 4;
|
||||||
|
const PHASE_TEARDOWN = 5;
|
||||||
|
|
||||||
|
protected static int $component_phase = self::PHASE_NONE;
|
||||||
|
|
||||||
|
static function set_component_phase(int $component_phase): void {
|
||||||
|
self::$component_phase = $component_phase;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function get_component_phase(): int {
|
||||||
|
return self::$component_phase;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static ?IRenderer $renderer = null;
|
||||||
|
|
||||||
|
static function set_renderer(IRenderer $renderer): void {
|
||||||
|
self::$renderer = $renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function get_renderer(): ?IRenderer {
|
||||||
|
return self::$renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static bool $html_output = false;
|
||||||
|
|
||||||
|
static function set_html_output(): void {
|
||||||
|
self::$html_output = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function is_html_output(): bool {
|
||||||
|
return self::$html_output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,27 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace nulib\web;
|
|
||||||
|
|
||||||
use nulib\web\base\RouteManager;
|
|
||||||
|
|
||||||
class route {
|
|
||||||
protected static RouteManager $route;
|
|
||||||
|
|
||||||
const MODE_EXACT = RouteManager::MODE_EXACT;
|
|
||||||
const MODE_PREFIX = RouteManager::MODE_PREFIX;
|
|
||||||
const MODE_PACKAGE = RouteManager::MODE_PACKAGE;
|
|
||||||
const MODE_PACKAGE2 = RouteManager::MODE_PACKAGE2;
|
|
||||||
|
|
||||||
static final function add(array ...$routes): void { self::$route->addRoute(...$routes); }
|
|
||||||
|
|
||||||
static final function set_error($page): void { self::$route->setErrorPage($page); }
|
|
||||||
static final function get_error($error): array { return self::$route->getErrorPage($error); }
|
|
||||||
|
|
||||||
static final function get_page(?string $path=null): array { return self::$route->getPage($path); }
|
|
||||||
static final function get_path($page): string { return self::$route->getPath($page); }
|
|
||||||
}
|
|
||||||
|
|
||||||
new class extends route {
|
|
||||||
function __construct() {
|
|
||||||
self::$route = new RouteManager();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Loading…
x
Reference in New Issue
Block a user