Compare commits

...

12 Commits

32 changed files with 923 additions and 401 deletions

View File

@ -17,6 +17,21 @@
</DockerContainerSettings> </DockerContainerSettings>
</value> </value>
</entry> </entry>
<entry key="385aa179-8c50-45e2-9ad7-4d9bfba298a3">
<value>
<DockerContainerSettings>
<option name="version" value="1" />
<option name="volumeBindings">
<list>
<DockerVolumeBindingImpl>
<option name="containerPath" value="/opt/project" />
<option name="hostPath" value="$PROJECT_DIR$" />
</DockerVolumeBindingImpl>
</list>
</option>
</DockerContainerSettings>
</value>
</entry>
<entry key="38915385-b3ff-4f4b-8a9a-d5f3ecae559e"> <entry key="38915385-b3ff-4f4b-8a9a-d5f3ecae559e">
<value> <value>
<DockerContainerSettings> <DockerContainerSettings>

4
.idea/php.xml generated
View File

@ -2,7 +2,7 @@
<project version="4"> <project version="4">
<component name="MessDetector"> <component name="MessDetector">
<phpmd_settings> <phpmd_settings>
<phpmd_by_interpreter asDefaultInterpreter="true" interpreter_id="846389f7-9fb5-4173-a868-1dc6b8fbb3fa" timeout="30000" /> <phpmd_by_interpreter asDefaultInterpreter="true" interpreter_id="38915385-b3ff-4f4b-8a9a-d5f3ecae559e" timeout="30000" />
</phpmd_settings> </phpmd_settings>
</component> </component>
<component name="MessDetectorOptionsConfiguration"> <component name="MessDetectorOptionsConfiguration">
@ -17,7 +17,7 @@
</component> </component>
<component name="PhpCodeSniffer"> <component name="PhpCodeSniffer">
<phpcs_settings> <phpcs_settings>
<phpcs_by_interpreter asDefaultInterpreter="true" interpreter_id="846389f7-9fb5-4173-a868-1dc6b8fbb3fa" timeout="30000" /> <phpcs_by_interpreter asDefaultInterpreter="true" interpreter_id="38915385-b3ff-4f4b-8a9a-d5f3ecae559e" timeout="30000" />
</phpcs_settings> </phpcs_settings>
</component> </component>
<component name="PhpIncludePathManager"> <component name="PhpIncludePathManager">

View File

@ -19,6 +19,7 @@ while true; do
fi fi
cd .. cd ..
done done
cd "$owd"
export RUNPHP_MOUNT= export RUNPHP_MOUNT=
if [ "$MYNAME" == composer ]; then if [ "$MYNAME" == composer ]; then

View File

@ -3,6 +3,7 @@ namespace nulib\cache;
use Exception; use Exception;
use nulib\cv; use nulib\cv;
use nulib\exceptions;
use nulib\ext\utils; use nulib\ext\utils;
use nulib\file\SharedFile; use nulib\file\SharedFile;
use nulib\os\path; use nulib\os\path;
@ -178,13 +179,14 @@ class CacheFile extends SharedFile {
} }
protected function afterAction() { protected function afterAction() {
# égalité non stricte pour start et duration
$modified = false; $modified = false;
if ($this->start != $this->ostart) $modified = true; if ($this->start != $this->ostart) $modified = true;
$duration = $this->duration; $duration = $this->duration;
$oduration = $this->oduration; $oduration = $this->oduration;
if ($duration === null || $oduration === null) $modified = true; if ($duration === null || $oduration === null) $modified = true;
elseif ($duration->getDest() != $oduration->getDest()) $modified = true; elseif ($duration->getDest() != $oduration->getDest()) $modified = true;
# égalité stricte uniquement pour $data et $datafiles # égalité stricte pour $data
if ($this->data !== $this->odata) $modified = true; if ($this->data !== $this->odata) $modified = true;
if ($modified && !$this->readonly) { if ($modified && !$this->readonly) {
$this->lockWrite(); $this->lockWrite();
@ -288,20 +290,20 @@ class CacheFile extends SharedFile {
return $this; return $this;
} }
function get($data=null, bool $noCache=false) { function get($key=null, bool $noCache=false) {
return $this->action(function () use ($data, $noCache) { return $this->action(function () use ($key, $noCache) {
return $this->refreshData($data, $noCache); return $this->refreshData($key, $noCache);
}); });
} }
function all($data=null, bool $noCache=false): ?iterable { function all($key=null, bool $noCache=false): ?iterable {
$data = $this->get($data, $noCache); $data = $this->get($key, $noCache);
if ($data !== null && !is_iterable($data)) $data = [$data]; if ($data !== null && !is_iterable($data)) $data = [$data];
return $data; return $data;
} }
function delete($data=null): void { function delete($key=null): void {
$source = $this->sources[$data] ?? null; $source = $this->sources[$key] ?? null;
if ($source !== null) $source->delete(); if ($source !== null) $source->delete();
} }

View File

@ -58,8 +58,8 @@ class file {
return $file; return $file;
} }
static function writer($output, ?string $mode="w+b", ?callable $func=null): FileWriter { static function writer($output, ?callable $func=null): FileWriter {
$file = new FileWriter(self::fix_dash($output), $mode); $file = new FileWriter(self::fix_dash($output), "w+b");
if ($func !== null) { if ($func !== null) {
try { try {
$func($file); $func($file);

View File

@ -1,7 +1,9 @@
<?php <?php
namespace nulib\mail; namespace nulib\mail;
use nulib\app\config;
use nulib\cl; use nulib\cl;
use nulib\web\session;
class MailTemplateHelper { class MailTemplateHelper {
function __construct(?array $data) { function __construct(?array $data) {

View File

@ -11,9 +11,14 @@ trait _TMessenger {
static function set_messenger(IMessenger $msg, bool $replace=false): IMessenger { static function set_messenger(IMessenger $msg, bool $replace=false): IMessenger {
if (self::$msg instanceof NullMessenger) self::$msg = null; if (self::$msg instanceof NullMessenger) self::$msg = null;
if ($replace || self::$msg === null) return self::$msg = $msg; if ($replace || self::$msg === null) {
elseif (self::$msg instanceof ProxyMessenger) self::$msg->addMessenger($msg); return self::$msg = $msg;
else self::$msg = new ProxyMessenger(self::$msg); } elseif (self::$msg instanceof ProxyMessenger) {
self::$msg->addMessenger($msg);
} else {
self::$msg = new ProxyMessenger(self::$msg);
self::$msg->addMessenger($msg);
}
return $msg; return $msg;
} }

View File

@ -1,8 +1,6 @@
<?php <?php
namespace nulib\output; namespace nulib\output;
use nulib\app\app;
use nulib\exceptions;
use nulib\output\std\ConsoleMessenger; use nulib\output\std\ConsoleMessenger;
/** /**

View File

@ -2,7 +2,6 @@
namespace nulib\output; namespace nulib\output;
use nulib\output\std\LogMessenger; use nulib\output\std\LogMessenger;
use nulib\output\std\NullMessenger;
use nulib\output\std\ProxyMessenger; use nulib\output\std\ProxyMessenger;
/** /**

View File

@ -49,8 +49,12 @@ abstract class AbstractMessenger implements _IMessenger {
protected int $lastTitleId = 1; protected int $lastTitleId = 1;
protected abstract function title__getId(): ?int;
protected int $lastActionId = 1; protected int $lastActionId = 1;
protected abstract function action__getId(): ?int;
protected function getLinePrefix(): ?string { protected function getLinePrefix(): ?string {
$linePrefix = null; $linePrefix = null;
if ($this->addDate) { if ($this->addDate) {
@ -59,9 +63,9 @@ abstract class AbstractMessenger implements _IMessenger {
} }
if ($this->showIds) { if ($this->showIds) {
if ($this->id !== null) $linePrefix .= "p=$this->id "; if ($this->id !== null) $linePrefix .= "p=$this->id ";
$titleId = $this->_getTitleId(); $titleId = $this->title__getId();
if ($titleId !== null) $linePrefix .= "t=$titleId "; if ($titleId !== null) $linePrefix .= "t=$titleId ";
$actionId = $this->_getActionId(); $actionId = $this->action__getId();
if ($actionId !== null) $linePrefix .= "a=$actionId "; if ($actionId !== null) $linePrefix .= "a=$actionId ";
} }
return $linePrefix; return $linePrefix;
@ -80,9 +84,8 @@ abstract class AbstractMessenger implements _IMessenger {
} }
protected function _printTitle( protected function _printTitle(
?string $linePrefix, int $level, int $level, string $type, ?string $linePrefix, int $indentLevel,
string $type, $content, StdOutput $out, $content
int $indentLevel, StdOutput $out
): void { ): void {
$prefixes = self::GENERIC_PREFIXES[$level][$type]; $prefixes = self::GENERIC_PREFIXES[$level][$type];
if ($prefixes[0]) $out->print(); if ($prefixes[0]) $out->print();
@ -129,13 +132,13 @@ abstract class AbstractMessenger implements _IMessenger {
} }
} }
protected abstract function flushActions(bool $endAction=false, ?int $overrideLevel=null): void; protected abstract function action__flush(bool $endAction=false, ?int $overrideLevel=null): void;
protected function _printAction( protected function _printAction(
?string $linePrefix, int $level, int $level, ?string $linePrefix, int $indentLevel,
StdOutput $out,
bool $printContent, $content, bool $printContent, $content,
bool $printResult, ?bool $rsuccess, $rcontent, bool $printResult, ?bool $rsuccess, $rcontent
int $indentLevel, StdOutput $out
): void { ): void {
$color = $out->isColor(); $color = $out->isColor();
if ($rsuccess === true) $type = "success"; if ($rsuccess === true) $type = "success";
@ -207,9 +210,8 @@ abstract class AbstractMessenger implements _IMessenger {
} }
protected function _printGeneric( protected function _printGeneric(
?string $linePrefix, int $level, int $level, string $type, ?string $linePrefix, int $indentLevel,
string $type, $content, StdOutput $out, $content
int $indentLevel, StdOutput $out
): void { ): void {
$prefixes = self::GENERIC_PREFIXES[$level][$type]; $prefixes = self::GENERIC_PREFIXES[$level][$type];
$content = cl::with($content); $content = cl::with($content);
@ -242,9 +244,8 @@ abstract class AbstractMessenger implements _IMessenger {
} }
protected function _printGenericOrException( protected function _printGenericOrException(
?int $level, ?int $level, string $type, int $indentLevel,
string $type, $content, StdOutput $out, $content
int $indentLevel, StdOutput $out
): void { ): void {
$linePrefix = $this->getLinePrefix(); $linePrefix = $this->getLinePrefix();
# si $content contient des exceptions, les afficher avec un level moindre # si $content contient des exceptions, les afficher avec un level moindre
@ -269,8 +270,8 @@ abstract class AbstractMessenger implements _IMessenger {
$flushActions = true; $flushActions = true;
$showContent = $this->checkLevel($level); $showContent = $this->checkLevel($level);
if ($content !== null && $showContent) { if ($content !== null && $showContent) {
$this->flushActions(); $flushActions = false; $this->action__flush(); $flushActions = false;
$this->_printGeneric($linePrefix, $level, $type, $content, $indentLevel, $out); $this->_printGeneric($level, $type, $linePrefix, $indentLevel, $out, $content);
} }
if ($exceptions !== null) { if ($exceptions !== null) {
$level1 = $this->decrLevel($level); $level1 = $this->decrLevel($level);
@ -279,16 +280,16 @@ abstract class AbstractMessenger implements _IMessenger {
# tout d'abord message # tout d'abord message
$message = exceptions::get_message($exception); $message = exceptions::get_message($exception);
if ($showContent) { if ($showContent) {
if ($flushActions) { $this->flushActions(); $flushActions = false; } if ($flushActions) { $this->action__flush(); $flushActions = false; }
$this->_printGeneric($linePrefix, $level, $type, $message, $indentLevel, $out); $this->_printGeneric($level, $type, $linePrefix, $indentLevel, $out, $message);
} }
# puis summary et traceback # puis summary et traceback
if ($showTraceback) { if ($showTraceback) {
if ($flushActions) { $this->flushActions(); $flushActions = false; } if ($flushActions) { $this->action__flush(); $flushActions = false; }
$summary = exceptions::get_summary($exception, false); $summary = exceptions::get_summary($exception, false);
$this->_printGeneric($linePrefix, $level1, $type, $summary, $indentLevel, $out); $this->_printGeneric($level1, $type, $linePrefix, $indentLevel, $out, $summary);
$traceback = exceptions::get_traceback($exception); $traceback = exceptions::get_traceback($exception);
$this->_printGeneric($linePrefix, $level1, $type, $traceback, $indentLevel, $out); $this->_printGeneric($level1, $type, $linePrefix, $indentLevel, $out, $traceback);
} }
} }
} }

View File

@ -108,13 +108,24 @@ class ConsoleMessenger extends AbstractMessenger {
/** @var array section qui est en attente d'affichage */ /** @var array section qui est en attente d'affichage */
protected ?array $section; protected ?array $section;
protected function section__end(): void {
while ($this->actions) $this->adone();
while ($this->titles) $this->title__end();
$this->inSection = false;
$this->section = null;
}
function section__afterFunc(): void {
$this->section__end();
}
function section($content, ?callable $func=null, ?int $level=null): void { function section($content, ?callable $func=null, ?int $level=null): void {
$this->_endSection(); $this->section__end();
$this->inSection = true; $this->inSection = true;
if (!$this->checkLevel($level)) return; if (!$this->checkLevel($level)) return;
$this->section = [ $this->section = [
"msg_level" => $level,
"line_prefix" => $this->getLinePrefix(), "line_prefix" => $this->getLinePrefix(),
"level" => $level,
"content" => $content, "content" => $content,
"print_content" => true, "print_content" => true,
]; ];
@ -122,7 +133,7 @@ class ConsoleMessenger extends AbstractMessenger {
try { try {
$func($this); $func($this);
} finally { } finally {
$this->_endSection(); $this->section__afterFunc();
} }
} }
} }
@ -131,26 +142,18 @@ class ConsoleMessenger extends AbstractMessenger {
$section =& $this->section; $section =& $this->section;
if ($section !== null && $section["print_content"]) { if ($section !== null && $section["print_content"]) {
$this->_printTitle( $this->_printTitle(
$section["line_prefix"], $section["level"], $section["msg_level"], "section", $section["line_prefix"], 0,
"section", $section["content"], $this->err, $section["content"]);
0, $this->err);
$section["print_content"] = false; $section["print_content"] = false;
} }
} }
function _endSection(): void {
while ($this->actions) $this->adone();
while ($this->titles) $this->_endTitle();
$this->inSection = false;
$this->section = null;
}
protected function getIndentLevel(bool $withActions=true): int { protected function getIndentLevel(bool $withActions=true): int {
$indentLevel = count($this->titles) - 1; $indentLevel = count($this->titles) - 1;
if ($indentLevel < 0) $indentLevel = 0; if ($indentLevel < 0) $indentLevel = 0;
if ($withActions) { if ($withActions) {
foreach ($this->actions as $action) { foreach ($this->actions as $action) {
if ($action["level"] < $this->minLevel) continue; if ($action["msg_level"] < $this->minLevel) continue;
$indentLevel++; $indentLevel++;
} }
} }
@ -159,80 +162,46 @@ class ConsoleMessenger extends AbstractMessenger {
protected array $titles; protected array $titles;
function _getTitleMark(): int { protected function title__last(): ?array {
return count($this->titles); $last = end($this->titles);
return $last !== false? $last: null;
} }
function _getTitleId(): ?int { function title__getMarks(): array {
return end($this->titles)["id"] ?? null; return [count($this->titles)];
} }
function title($content, ?callable $func=null, ?int $level=null): void { protected function title__getId(): ?int {
if (!$this->checkLevel($level)) return; return $this->title__last()["id"] ?? null;
$titleLevel = $this->_getTitleMark(); }
// faire en deux temps pour linePrefix soit à jour
$this->titles[] = ["id" => $this->lastTitleId++]; protected function title__end(?int $until=null): void {
A::merge($this->titles[array_key_last($this->titles)], [ $title = $this->title__last();
"title_level" => $titleLevel, if ($title !== null) {
"max_title_level" => null, $until ??= $title["max_title_level"];
"line_prefix" => $this->getLinePrefix(), $until ??= $this->title__getMarks()[0] - 1;
"level" => $level, while (count($this->titles) > $until) {
"content" => $content, array_pop($this->titles);
"print_content" => true,
"descs" => [],
"print_descs" => false,
]);
if ($func !== null) {
try {
$title =& $this->titles[array_key_last($this->titles)];
$title["max_title_level"] = $titleLevel + 1;
$func($this);
} finally {
$title["max_title_level"] = null;
$this->_endTitle($titleLevel);
} }
} }
} }
function desc($content, ?int $level=null): void { protected function title__flush(): void {
if (!$this->checkLevel($level)) return;
$desc = [
"line_prefix" => $this->getLinePrefix(),
"level" => $level,
"content" => $content,
];
$key = array_key_last($this->titles);
if ($key !== null) {
$title =& $this->titles[$key];
$title["descs"][] = $desc;
$title["print_descs"] = true;
} else {
# pas de titre en cours
$this->_printGeneric(
$desc["line_prefix"], $desc["level"],
"desc", $desc["content"],
0, $this->err);
}
}
protected function flushTitles(): void {
$this->printSection(); $this->printSection();
$err = $this->err; $err = $this->err;
$indentLevel = 0; $indentLevel = 0;
foreach ($this->titles as &$title) { foreach ($this->titles as &$title) {
if ($title["print_content"]) { if ($title["print_content"]) {
$this->_printTitle( $this->_printTitle(
$title["line_prefix"], $title["level"], $title["msg_level"], "title", $title["line_prefix"], $indentLevel,
"title", $title["content"], $err, $title["content"]);
$indentLevel, $err);
$title["print_content"] = false; $title["print_content"] = false;
} }
if ($title["print_descs"]) { if ($title["print_descs"]) {
foreach ($title["descs"] as $desc) { foreach ($title["descs"] as $desc) {
$this->_printGeneric( $this->_printGeneric(
$desc["line_prefix"], $desc["level"], $desc["msg_level"], "desc", $desc["line_prefix"], $indentLevel,
"desc", $desc["content"], $err, $desc["content"]);
$indentLevel, $err);
} }
$title["descs"] = []; $title["descs"] = [];
$title["print_descs"] = false; $title["print_descs"] = false;
@ -241,35 +210,102 @@ class ConsoleMessenger extends AbstractMessenger {
}; unset($title); }; unset($title);
} }
function _endTitle(?int $until=null): void { protected function &title__ref(): ?array {
$title = $this->titles[array_key_last($this->titles)]; return $this->titles[array_key_last($this->titles)];
$until ??= $title["max_title_level"]; }
$until ??= $this->_getTitleMark() - 1;
while (count($this->titles) > $until) { function title__beforeFunc(array $marks): void {
array_pop($this->titles); $title =& $this->title__ref();
$title["max_title_level"] = $marks[0] + 1;
}
function title__afterFunc(array $marks): void {
$title =& $this->title__ref();
$title["max_title_level"] = null;
$this->title__end($marks[0]);
}
function title($content, ?callable $func=null, ?int $level=null): void {
if (!$this->checkLevel($level)) return;
$marks = $this->title__getMarks();
// faire en deux temps pour linePrefix soit à jour
$this->titles[] = ["id" => $this->lastTitleId++];
A::merge($this->title__ref(), [
"title_level" => $marks[0],
"max_title_level" => null,
"msg_level" => $level,
"line_prefix" => $this->getLinePrefix(),
"content" => $content,
"print_content" => true,
"descs" => [],
"print_descs" => false,
]);
if ($func !== null) {
try {
$this->title__beforeFunc($marks);
$func($this);
} finally {
$this->title__afterFunc($marks);
}
}
}
function desc($content, ?int $level=null): void {
if (!$this->checkLevel($level)) return;
$desc = [
"msg_level" => $level,
"line_prefix" => $this->getLinePrefix(),
"content" => $content,
];
$title = $this->title__last();
if ($title !== null) {
$title =& $this->title__ref();
$title["descs"][] = $desc;
$title["print_descs"] = true;
} else {
# pas de titre en cours
$this->_printGeneric(
$desc["msg_level"], "desc", $desc["line_prefix"], 0,
$this->err, $desc["content"]);
} }
} }
protected array $actions; protected array $actions;
function _getActionMark(): int { protected function action__last(): ?array {
return count($this->actions); $last = end($this->actions);
return $last !== false? $last: null;
} }
function _getActionId(): ?int { function action__getMarks(): array {
return end($this->actions)["id"] ?? null; return [count($this->actions)];
} }
protected function flushActions(bool $endAction=false, ?int $overrideLevel=null): void { protected function action__getId(): ?int {
$this->flushTitles(); return $this->action__last()["id"] ?? null;
}
protected function action__end(?int $until=null): void {
$action = $this->action__last();
if ($action !== null) {
$until ??= $action["max_action_level"];
$until ??= $this->action__getMarks()[0] - 1;
while (count($this->actions) > $until) {
array_pop($this->actions);
}
}
}
protected function action__flush(bool $endAction=false, ?int $overrideLevel=null): void {
$this->title__flush();
$err = $this->err; $err = $this->err;
$indentLevel = $this->getIndentLevel(false); $indentLevel = $this->getIndentLevel(false);
$lastIndex = array_key_last($this->actions); $lastIndex = array_key_last($this->actions);
$index = 0; $index = 0;
foreach ($this->actions as &$action) { foreach ($this->actions as &$action) {
$mergeResult = $index++ == $lastIndex && $endAction; $mergeResult = $index++ == $lastIndex && $endAction;
$level = $overrideLevel?? $action["msg_level"];
$linePrefix = $action["line_prefix"]; $linePrefix = $action["line_prefix"];
$level = $overrideLevel?? $action["level"];
$content = $action["content"]; $content = $action["content"];
$printContent = $action["print_content"]; $printContent = $action["print_content"];
$rsuccess = $action["result_success"]; $rsuccess = $action["result_success"];
@ -278,51 +314,69 @@ class ConsoleMessenger extends AbstractMessenger {
if ($mergeResult) { if ($mergeResult) {
if (time() - $action["timestamp"] <= 2) { if (time() - $action["timestamp"] <= 2) {
$this->_printAction( $this->_printAction(
$linePrefix, $level, $level, $linePrefix, $indentLevel,
$err,
$printContent, $content, $printContent, $content,
true, $rsuccess, $rcontent, true, $rsuccess, $rcontent);
$indentLevel, $err);
} else { } else {
# si l'action a pris plus de 2 secondes, ne pas fusionner pour que # si l'action a pris plus de 2 secondes, ne pas fusionner pour que
# l'on voit le temps que ça a pris # l'on voit le temps que ça a pris
$this->_printAction( $this->_printAction(
$linePrefix, $level, $level, $linePrefix, $indentLevel,
$err,
$printContent, $content, $printContent, $content,
false, null, null, false, null, null);
$indentLevel, $err);
# recalculer une nouvelle ligne de préfixe pour le résultat # recalculer une nouvelle ligne de préfixe pour le résultat
$linePrefix = $this->getLinePrefix(); $linePrefix = $this->getLinePrefix();
$this->_printAction( $this->_printAction(
$linePrefix, $level, $level, $linePrefix, $indentLevel,
$err,
false, null, false, null,
true, $rsuccess, $rcontent, true, $rsuccess, $rcontent);
$indentLevel, $err);
} }
$action["action_aresult"] = true;
} elseif ($printContent) { } elseif ($printContent) {
$this->_printAction( $this->_printAction(
$linePrefix, $level, $level, $linePrefix, $indentLevel,
$err,
$printContent, $content, $printContent, $content,
false, $rsuccess, $rcontent, false, $rsuccess, $rcontent);
$indentLevel, $err);
$action["print_content"] = false; $action["print_content"] = false;
} }
$indentLevel++; $indentLevel++;
}; unset($action); }; unset($action);
if ($endAction) $this->_endAction(); if ($endAction) $this->action__end();
}
protected function &action__ref(): ?array {
return $this->actions[array_key_last($this->actions)];
}
function action__beforeFunc(array $marks): void {
$action =& $this->action__ref();
$action["max_action_level"] = $marks[0] + 1;
}
function action__afterFunc(array $marks, $result): void {
$action =& $this->action__ref();
$aresult = $action["action_aresult"] ?? false;
if (!$aresult) $this->aresult($result);
$action["max_action_level"] = null;
$this->action__end($marks[0]);
} }
function action($content, ?callable $func=null, ?int $level=null): void { function action($content, ?callable $func=null, ?int $level=null): void {
$this->checkLevel($level); $this->checkLevel($level);
$actionLevel = $this->_getActionMark(); $marks = $this->action__getMarks();
// faire en deux temps pour linePrefix soit à jour // faire en deux temps pour linePrefix soit à jour
$this->actions[] = ["id" => $this->lastActionId++]; $this->actions[] = ["id" => $this->lastActionId++];
A::merge($this->actions[array_key_last($this->actions)], [ A::merge($this->action__ref(), [
"action_level" => $actionLevel, "action_level" => $marks[0],
"max_action_level" => null, "max_action_level" => null,
"action_aresult" => false,
"timestamp" => time(), "timestamp" => time(),
"msg_level" => $level,
"line_prefix" => $this->getLinePrefix(), "line_prefix" => $this->getLinePrefix(),
"level" => $level,
"content" => $content, "content" => $content,
"print_content" => true, "print_content" => true,
"result_success" => null, "result_success" => null,
@ -330,50 +384,46 @@ class ConsoleMessenger extends AbstractMessenger {
]); ]);
if ($func !== null) { if ($func !== null) {
try { try {
$action =& $this->actions[array_key_last($this->actions)]; $result = null;
$action["max_action_level"] = $actionLevel + 1; $this->action__beforeFunc($marks);
$result = $func($this); $result = $func($this);
if ($this->_getActionMark() > $actionLevel) {
$this->aresult($result);
}
} catch (Exception $e) { } catch (Exception $e) {
$this->afailure($e); $this->afailure($e);
throw $e; throw $e;
} finally { } finally {
$action["max_action_level"] = null; $this->action__afterFunc($marks, $result);
$this->_endAction($actionLevel);
} }
} }
} }
function step($content, ?int $level=null): void { function step($content, ?int $level=null): void {
$this->_printGenericOrException( $this->_printGenericOrException(
$level, "step", $content, $level, "step", $this->getIndentLevel(),
$this->getIndentLevel(), $this->err); $this->err, $content);
} }
function asuccess($content=null, ?int $overrideLevel=null): void { function asuccess($content=null, ?int $overrideLevel=null): void {
if (!$this->actions) $this->action(null); if (!$this->actions) $this->action(null);
$action =& $this->actions[array_key_last($this->actions)]; $action =& $this->action__ref();
$action["result_success"] = true; $action["result_success"] = true;
$action["result_content"] = $content; $action["result_content"] = $content;
$this->flushActions(true, $overrideLevel); $this->action__flush(true, $overrideLevel);
} }
function afailure($content=null, ?int $overrideLevel=null): void { function afailure($content=null, ?int $overrideLevel=null): void {
if (!$this->actions) $this->action(null); if (!$this->actions) $this->action(null);
$action =& $this->actions[array_key_last($this->actions)]; $action =& $this->action__ref();
$action["result_success"] = false; $action["result_success"] = false;
$action["result_content"] = $content; $action["result_content"] = $content;
$this->flushActions(true, $overrideLevel); $this->action__flush(true, $overrideLevel);
} }
function adone($content=null, ?int $overrideLevel=null): void { function adone($content=null, ?int $overrideLevel=null): void {
if (!$this->actions) $this->action(null); if (!$this->actions) $this->action(null);
$action =& $this->actions[array_key_last($this->actions)]; $action =& $this->action__ref();
$action["result_success"] = null; $action["result_success"] = null;
$action["result_content"] = $content; $action["result_content"] = $content;
$this->flushActions(true, $overrideLevel); $this->action__flush(true, $overrideLevel);
} }
function aresult($result=null, ?int $overrideLevel=null): void { function aresult($result=null, ?int $overrideLevel=null): void {
@ -384,49 +434,40 @@ class ConsoleMessenger extends AbstractMessenger {
else $this->adone($result, $overrideLevel); else $this->adone($result, $overrideLevel);
} }
function _endAction(?int $until=null): void {
$action = $this->actions[array_key_last($this->actions)];
$until ??= $action["max_action_level"];
$until ??= $this->_getActionMark() - 1;
while (count($this->actions) > $until) {
array_pop($this->actions);
}
}
function print($content, ?int $level=null): void { function print($content, ?int $level=null): void {
$this->_printGenericOrException( $this->_printGenericOrException(
$level, "print", $content, $level, "print", $this->getIndentLevel(),
$this->getIndentLevel(), $this->out); $this->out, $content);
} }
function info($content, ?int $level=null): void { function info($content, ?int $level=null): void {
$this->_printGenericOrException( $this->_printGenericOrException(
$level, "info", $content, $level, "info", $this->getIndentLevel(),
$this->getIndentLevel(), $this->err); $this->err, $content);
} }
function note($content, ?int $level=null): void { function note($content, ?int $level=null): void {
$this->_printGenericOrException( $this->_printGenericOrException(
$level, "note", $content, $level, "note", $this->getIndentLevel(),
$this->getIndentLevel(), $this->err); $this->err, $content);
} }
function warning($content, ?int $level=null): void { function warning($content, ?int $level=null): void {
$this->_printGenericOrException( $this->_printGenericOrException(
$level, "warning", $content, $level, "warning", $this->getIndentLevel(),
$this->getIndentLevel(), $this->err); $this->err, $content);
} }
function error($content, ?int $level=null): void { function error($content, ?int $level=null): void {
$this->_printGenericOrException( $this->_printGenericOrException(
$level, "error", $content, $level, "error", $this->getIndentLevel(),
$this->getIndentLevel(), $this->err); $this->err, $content);
} }
function end(bool $all=false): void { function end(bool $all=false): void {
if ($all) $this->_endSection(); if ($all) $this->section__afterFunc();
elseif ($this->actions) $this->_endAction(); elseif ($this->actions) $this->action__end();
elseif ($this->titles) $this->_endTitle(); elseif ($this->titles) $this->title__end();
else $this->_endSection(); else $this->section__afterFunc();
} }
} }

View File

@ -79,183 +79,227 @@ class LogMessenger extends AbstractMessenger {
return $clone; return $clone;
} }
protected function section__end(): void {
$this->end(true);
}
function section__afterFunc(): void {
$this->section__end();
}
function section($content, ?callable $func=null, ?int $level=null): void { function section($content, ?callable $func=null, ?int $level=null): void {
$this->_endSection(); $this->section__end();
if (!$this->checkLevel($level)) return; if (!$this->checkLevel($level)) return;
$this->_printTitle( $this->_printTitle(
$this->getLinePrefix(), $level, $level, "section", $this->getLinePrefix(), 0,
"section", $content, $this->out, $content);
0, $this->out);
if ($func !== null) { if ($func !== null) {
try { try {
$func($this); $func($this);
} finally { } finally {
$this->_endSection(); $this->section__afterFunc();
} }
} }
} }
function _endSection(): void {
$this->end(true);
}
protected array $titles; protected array $titles;
function _getTitleMark(): int { protected function title__last(): ?array {
return count($this->titles); $last = end($this->titles);
return $last !== false? $last: null;
} }
function _getTitleId(): ?int { function title__getMarks(): array {
return end($this->titles)["id"] ?? null; return [count($this->titles)];
}
protected function title__getId(): ?int {
return $this->title__last()["id"] ?? null;
}
protected function title__end(?int $until=null): void {
$title = $this->title__last();
if ($title !== null) {
$until ??= $title["max_title_level"];
$until ??= $this->title__getMarks()[0] - 1;
while (count($this->titles) > $until) {
array_pop($this->titles);
}
}
}
protected function &title__ref(): ?array {
return $this->titles[array_key_last($this->titles)];
}
function title__beforeFunc(array $marks): void {
$title =& $this->title__ref();
$title["max_title_level"] = $marks[0] + 1;
}
function title__afterFunc(array $marks): void {
$title =& $this->title__ref();
$title["max_title_level"] = null;
$this->title__end($marks[0]);
} }
function title($content, ?callable $func=null, ?int $level=null): void { function title($content, ?callable $func=null, ?int $level=null): void {
if (!$this->checkLevel($level)) return; if (!$this->checkLevel($level)) return;
$titleLevel = $this->_getTitleMark(); $marks = $this->title__getMarks();
$this->titles[] = [ $this->titles[] = [
"id" => $this->lastTitleId++, "id" => $this->lastTitleId++,
"title_level" => $titleLevel, "title_level" => $marks[0],
"max_title_level" => null, "max_title_level" => null,
]; ];
$this->_printTitle( $this->_printTitle(
$this->getLinePrefix(), $level, $level, "title", $this->getLinePrefix(), $marks[0],
"title", $content, $this->out, $content);
$titleLevel, $this->out);
if ($func !== null) { if ($func !== null) {
try { try {
$title =& $this->titles[array_key_last($this->titles)]; $this->title__beforeFunc($marks);
$title["max_title_level"] = $titleLevel + 1;
$func($this); $func($this);
} finally { } finally {
$title["max_title_level"] = null; $this->title__afterFunc($marks);
$this->_endTitle($titleLevel);
} }
} }
} }
function desc($content, ?int $level=null): void { function desc($content, ?int $level=null): void {
if (!$this->checkLevel($level)) return; if (!$this->checkLevel($level)) return;
$titleLevel = end($this->titles)["title_level"] ?? 0; $titleLevel = $this->title__last()["title_level"] ?? 0;
$this->_printGeneric( $this->_printGeneric(
$this->getLinePrefix(), $level, $level, "desc", $this->getLinePrefix(), $titleLevel,
"desc", $content, $this->out, $content);
$titleLevel, $this->out);
}
function _endTitle(?int $until=null): void {
$title = $this->titles[array_key_last($this->titles)];
$until ??= $title["max_title_level"];
$until ??= $this->_getTitleMark() - 1;
while (count($this->titles) > $until) {
array_pop($this->titles);
}
} }
protected array $actions; protected array $actions;
function _getActionMark(): int { protected function action__last(): ?array {
return count($this->actions); $last = end($this->actions);
return $last !== false? $last: null;
} }
function _getActionId(): ?int { function action__getMarks(): array {
return end($this->actions)["id"] ?? null; return [count($this->actions)];
}
protected function action__getId(): ?int {
return $this->action__last()["id"] ?? null;
}
protected function action__end(?int $until=null): void {
$action = $this->action__last();
if ($action !== null) {
$until ??= $action["max_action_level"];
$until ??= $this->action__getMarks()[0] - 1;
while (count($this->actions) > $until) {
array_pop($this->actions);
}
}
}
protected function action__flush(bool $endAction=false, ?int $overrideLevel=null): void {
}
protected function &action__ref(): ?array {
return $this->actions[array_key_last($this->actions)];
}
function action__beforeFunc(array $marks): void {
$action =& $this->action__ref();
$action["max_action_level"] = $marks[0] + 1;
}
function action__afterFunc(array $marks, $result): void {
$action =& $this->action__ref();
$aresult = $action["action_aresult"] ?? false;
if (!$aresult) $this->aresult($result);
$action["max_action_level"] = null;
$this->action__end($marks[0]);
} }
function action($content, ?callable $func=null, ?int $level=null): void { function action($content, ?callable $func=null, ?int $level=null): void {
$this->checkLevel($level); $this->checkLevel($level);
$actionLevel = $this->_getActionMark(); $marks = $this->action__getMarks();
$this->actions[] = [ $this->actions[] = [
"id" => $this->lastActionId++, "id" => $this->lastActionId++,
"action_level" => $actionLevel, "action_level" => $marks[0],
"max_action_level" => null, "max_action_level" => null,
"level" => $level "action_aresult" => false,
"msg_level" => $level
]; ];
$this->_printAction( $this->_printAction(
$this->getLinePrefix(), $level, $level, $this->getLinePrefix(), $marks[0],
$this->out,
true, $content, true, $content,
false, null, null, false, null, null);
$actionLevel, $this->out);
if ($func !== null) { if ($func !== null) {
try { try {
$action =& $this->actions[array_key_last($this->actions)]; $result = null;
$action["max_action_level"] = $actionLevel + 1; $this->action__beforeFunc($marks);
$result = $func($this); $result = $func($this);
if ($this->_getActionMark() > $actionLevel) {
$this->aresult($result);
}
} catch (Exception $e) { } catch (Exception $e) {
$this->afailure($e); $this->afailure($e);
throw $e; throw $e;
} finally { } finally {
$action["max_action_level"] = null; $this->action__afterFunc($marks, $result);
$this->_endAction($actionLevel);
} }
} }
} }
protected function flushActions(bool $endAction=false, ?int $overrideLevel=null): void {
}
function step($content, ?int $level=null): void { function step($content, ?int $level=null): void {
$this->_printGenericOrException( $this->_printGenericOrException(
$level, "step", $content, $level, "step", $this->getIndentLevel(),
$this->getIndentLevel(), $this->out); $this->out, $content);
} }
function asuccess($content=null, ?int $overrideLevel=null): void { function asuccess($content=null, ?int $overrideLevel=null): void {
if ($this->_getActionMark() == 0) $this->action(null); if ($this->action__getMarks()[0] == 0) $this->action(null);
$action = end($this->actions); $action =& $this->action__ref();
$level = $overrideLevel ?? $action["level"]; $level = $overrideLevel ?? $action["msg_level"];
$this->_printAction( $this->_printAction(
$this->getLinePrefix(), $level, $level, $this->getLinePrefix(), $action["action_level"],
$this->out,
false, null, false, null,
true, true, $content, true, true, $content);
$action["action_level"], $this->out); $action["action_aresult"] = true;
$this->_endAction(); $this->action__end();
} }
function afailure($content=null, ?int $overrideLevel=null): void { function afailure($content=null, ?int $overrideLevel=null): void {
if ($this->_getActionMark() == 0) $this->action(null); if ($this->action__getMarks()[0] == 0) $this->action(null);
$action = end($this->actions); $action =& $this->action__ref();
$level = $overrideLevel ?? $action["level"]; $level = $overrideLevel ?? $action["msg_level"];
$this->_printAction( $this->_printAction(
$this->getLinePrefix(), $level, $level, $this->getLinePrefix(), $action["action_level"],
$this->out,
false, null, false, null,
true, false, $content, true, false, $content);
$action["action_level"], $this->out); $action["action_aresult"] = true;
$this->_endAction(); $this->action__end();
} }
function adone($content=null, ?int $overrideLevel=null): void { function adone($content=null, ?int $overrideLevel=null): void {
if ($this->_getActionMark() == 0) $this->action(null); if ($this->action__getMarks()[0] == 0) $this->action(null);
$action = end($this->actions); $action =& $this->action__ref();
$level = $overrideLevel ?? $action["level"]; $level = $overrideLevel ?? $action["msg_level"];
$this->_printAction( $this->_printAction(
$this->getLinePrefix(), $level, $level, $this->getLinePrefix(), $action["action_level"],
$this->out,
false, null, false, null,
true, null, $content, true, null, $content);
$action["action_level"], $this->out); $action["action_aresult"] = true;
$this->_endAction(); $this->action__end();
} }
function aresult($result=null, ?int $overrideLevel=null): void { function aresult($result=null, ?int $overrideLevel=null): void {
if ($this->_getActionMark() == 0) $this->action(null); if ($this->action__getMarks()[0] == 0) $this->action(null);
if ($result === true) $this->asuccess(null, $overrideLevel); if ($result === true) $this->asuccess(null, $overrideLevel);
elseif ($result === false) $this->afailure(null, $overrideLevel); elseif ($result === false) $this->afailure(null, $overrideLevel);
elseif ($result instanceof Exception) $this->afailure($result, $overrideLevel); elseif ($result instanceof Exception) $this->afailure($result, $overrideLevel);
else $this->adone($result, $overrideLevel); else $this->adone($result, $overrideLevel);
} }
function _endAction(?int $until=null): void {
$action = $this->actions[array_key_last($this->actions)];
$until ??= $action["max_action_level"];
$until ??= $this->_getActionMark() - 1;
while (count($this->actions) > $until) {
array_pop($this->actions);
}
}
protected function getIndentLevel(bool $withActions=true): int { protected function getIndentLevel(bool $withActions=true): int {
$indentLevel = count($this->titles) - 1; $indentLevel = count($this->titles) - 1;
if ($indentLevel < 0) $indentLevel = 0; if ($indentLevel < 0) $indentLevel = 0;
@ -265,42 +309,42 @@ class LogMessenger extends AbstractMessenger {
function print($content, ?int $level=null): void { function print($content, ?int $level=null): void {
$this->_printGenericOrException( $this->_printGenericOrException(
$level, "print", $content, $level, "print", $this->getIndentLevel(),
$this->getIndentLevel(), $this->out); $this->out, $content);
} }
function info($content, ?int $level=null): void { function info($content, ?int $level=null): void {
$this->_printGenericOrException( $this->_printGenericOrException(
$level, "info", $content, $level, "info", $this->getIndentLevel(),
$this->getIndentLevel(), $this->out); $this->out, $content);
} }
function note($content, ?int $level=null): void { function note($content, ?int $level=null): void {
$this->_printGenericOrException( $this->_printGenericOrException(
$level, "note", $content, $level, "note", $this->getIndentLevel(),
$this->getIndentLevel(), $this->out); $this->out, $content);
} }
function warning($content, ?int $level=null): void { function warning($content, ?int $level=null): void {
$this->_printGenericOrException( $this->_printGenericOrException(
$level, "warning", $content, $level, "warning", $this->getIndentLevel(),
$this->getIndentLevel(), $this->out); $this->out, $content);
} }
function error($content, ?int $level=null): void { function error($content, ?int $level=null): void {
$this->_printGenericOrException( $this->_printGenericOrException(
$level, "error", $content, $level, "error", $this->getIndentLevel(),
$this->getIndentLevel(), $this->out); $this->out, $content);
} }
function end(bool $all=false): void { function end(bool $all=false): void {
if ($all) { if ($all) {
while ($this->actions) $this->adone(); while ($this->actions) $this->adone();
while ($this->titles) $this->_endTitle(); while ($this->titles) $this->title__end();
} elseif ($this->actions) { } elseif ($this->actions) {
$this->_endAction(); $this->action__end();
} elseif ($this->titles) { } elseif ($this->titles) {
$this->_endTitle(); $this->title__end();
} }
} }
} }

View File

@ -12,15 +12,18 @@ class NullMessenger implements IMessenger {
} }
function section($content, ?callable $func=null, ?int $level=null): void { function section($content, ?callable $func=null, ?int $level=null): void {
if ($func !== null) $func($this);
} }
function title($content, ?callable $func=null, ?int $level=null): void { function title($content, ?callable $func=null, ?int $level=null): void {
if ($func !== null) $func($this);
} }
function desc($content, ?int $level=null): void { function desc($content, ?int $level=null): void {
} }
function action($content, ?callable $func=null, ?int $level=null): void { function action($content, ?callable $func=null, ?int $level=null): void {
if ($func !== null) $func($this);
} }
function step($content, ?int $level=null): void { function step($content, ?int $level=null): void {

View File

@ -1,7 +1,6 @@
<?php <?php
namespace nulib\output\std; namespace nulib\output\std;
use Exception;
use nulib\output\IMessenger; use nulib\output\IMessenger;
/** /**
@ -10,14 +9,14 @@ use nulib\output\IMessenger;
* NB: si cette classe est instanciée sans argument, elle agit comme * NB: si cette classe est instanciée sans argument, elle agit comme
* {@link NullMessenger}: elle envoie tous les messages vers /dev/null * {@link NullMessenger}: elle envoie tous les messages vers /dev/null
*/ */
class ProxyMessenger implements IMessenger { class ProxyMessenger implements _IMessenger {
function __construct(?IMessenger ...$msgs) { function __construct(?IMessenger ...$msgs) {
foreach ($msgs as $msg) { foreach ($msgs as $msg) {
if ($msg !== null) $this->msgs[] = $msg; if ($msg !== null) $this->msgs[] = $msg;
} }
} }
/** @var IMessenger[] */ /** @var _IMessenger[] */
protected ?array $msgs = []; protected ?array $msgs = [];
function isEmpty(): bool { function isEmpty(): bool {
@ -43,45 +42,64 @@ class ProxyMessenger implements IMessenger {
return $clone; return $clone;
} }
function section__afterFunc(): void {
foreach ($this->msgs as $msg) {
if ($msg instanceof _IMessenger) {
$msg->section__afterFunc();
}
}
}
function section($content, ?callable $func=null, ?int $level=null): void { function section($content, ?callable $func=null, ?int $level=null): void {
$useFunc = false;
foreach ($this->msgs as $msg) { foreach ($this->msgs as $msg) {
$msg->section($content, null, $level); $msg->section($content, null, $level);
if ($msg instanceof _IMessenger) $useFunc = true;
} }
if ($useFunc && $func !== null) { if ($func !== null) {
try { try {
$func($this); $func($this);
} finally { } finally {
/** @var _IMessenger $msg */ $this->section__afterFunc();
foreach ($this->msgs as $msg) {
$msg->_endSection();
} }
} }
} }
function title__getMarks(): array {
$marks = [];
foreach ($this->msgs as $key => $msg) {
if ($msg instanceof _IMessenger) {
$marks[$key] = $msg->title__getMarks();
}
}
return $marks;
}
function title__beforeFunc(array $marks): void {
foreach ($this->msgs as $key => $msg) {
if ($msg instanceof _IMessenger) {
$msg->title__beforeFunc($marks[$key]);
}
}
}
function title__afterFunc(array $marks): void {
foreach ($this->msgs as $key => $msg) {
if ($msg instanceof _IMessenger) {
$msg->title__afterFunc($marks[$key]);
}
}
} }
function title($content, ?callable $func=null, ?int $level=null): void { function title($content, ?callable $func=null, ?int $level=null): void {
$useFunc = false; $marks = $this->title__getMarks();
$untils = [];
foreach ($this->msgs as $msg) { foreach ($this->msgs as $msg) {
if ($msg instanceof _IMessenger) {
$useFunc = true;
$untils[] = $msg->_getTitleMark();
}
$msg->title($content, null, $level); $msg->title($content, null, $level);
} }
if ($useFunc && $func !== null) { if ($func !== null) {
try { try {
$this->title__beforeFunc($marks);
$func($this); $func($this);
} finally { } finally {
/** @var _IMessenger $msg */ $this->title__afterFunc($marks);
$index = 0;
foreach ($this->msgs as $msg) {
if ($msg instanceof _IMessenger) {
$msg->_endTitle($untils[$index++]);
}
}
} }
} }
} }
@ -92,38 +110,44 @@ class ProxyMessenger implements IMessenger {
} }
} }
function action($content, ?callable $func=null, ?int $level=null): void { function action__getMarks(): array {
$useFunc = false; $marks = [];
$untils = []; foreach ($this->msgs as $key => $msg) {
foreach ($this->msgs as $msg) {
if ($msg instanceof _IMessenger) { if ($msg instanceof _IMessenger) {
$useFunc = true; $marks[$key] = $msg->action__getMarks();
$untils[] = $msg->_getActionMark();
} }
}
return $marks;
}
function action__beforeFunc(array $marks): void {
foreach ($this->msgs as $key => $msg) {
if ($msg instanceof _IMessenger) {
$msg->action__beforeFunc($marks[$key]);
}
}
}
function action__afterFunc(array $marks, $result): void {
foreach ($this->msgs as $key => $msg) {
if ($msg instanceof _IMessenger) {
$msg->action__afterFunc($marks[$key], $result);
}
}
}
function action($content, ?callable $func=null, ?int $level=null): void {
$marks = $this->action__getMarks();
foreach ($this->msgs as $msg) {
$msg->action($content, null, $level); $msg->action($content, null, $level);
} }
if ($useFunc && $func !== null) { if ($func !== null) {
try { try {
$result = null;
$this->action__beforeFunc($marks);
$result = $func($this); $result = $func($this);
/** @var _IMessenger $msg */
$index = 0;
foreach ($this->msgs as $msg) {
if ($msg->_getActionMark() > $untils[$index++]) {
$msg->aresult($result);
}
}
} catch (Exception $e) {
/** @var _IMessenger $msg */
foreach ($this->msgs as $msg) {
$msg->afailure($e);
}
throw $e;
} finally { } finally {
/** @var _IMessenger $msg */ $this->action__afterFunc($marks, $result);
$index = 0;
foreach ($this->msgs as $msg) {
$msg->_endAction($untils[$index++]);
}
} }
} }
} }

View File

@ -74,17 +74,19 @@ interface _IMessenger extends IMessenger {
"done" => [null, null], "done" => [null, null],
]; ];
function _endSection(): void; function section__afterFunc(): void;
function _getTitleMark(): int; /** @return int[] */
function title__getMarks(): array;
/** @param int[] $marks */
function title__beforeFunc(array $marks): void;
/** @param int[] $marks */
function title__afterFunc(array $marks): void;
function _getTitleId(): ?int; /** @return int[] */
function action__getMarks(): array;
function _endTitle(?int $until=null): void; /** @param int[] $marks */
function action__beforeFunc(array $marks): void;
function _getActionMark(): int; /** @param int[] $marks */
function action__afterFunc(array $marks, $result): void;
function _getActionId(): ?int;
function _endAction(?int $until=null): void;
} }

View File

@ -30,6 +30,11 @@ class Delay {
else return new static($delay, $from); else return new static($delay, $from);
} }
/**
* pour une durée infinie, l'intervalle est toujours de 1000 ans dans le futur
*/
const INF_INTERVAL = "P1000Y";
/** valeurs par défaut de x et y pour les unités supportées */ /** valeurs par défaut de x et y pour les unités supportées */
const DEFAULTS = [ const DEFAULTS = [
"w" => [0, 5], "w" => [0, 5],
@ -89,9 +94,9 @@ class Delay {
function __construct($delay, ?DateTimeInterface $from=null) { function __construct($delay, ?DateTimeInterface $from=null) {
$from = MutableDateTime::with($from)->clone(true); $from = MutableDateTime::with($from)->clone(true);
if ($delay === "INF") { if ($delay === null || $delay === "INF") {
$dest = $from; # $dest === null signifie un délai infini
$dest->add(new DateInterval("P9999Y")); $dest = null;
$repr = "INF"; $repr = "INF";
} elseif (is_int($delay)) { } elseif (is_int($delay)) {
[$dest, $repr] = self::compute_dest($delay, "s", null, $from); [$dest, $repr] = self::compute_dest($delay, "s", null, $from);
@ -115,38 +120,54 @@ class Delay {
} }
function __clone() { function __clone() {
if ($this->dest !== null) {
$this->dest = clone $this->dest; $this->dest = clone $this->dest;
} }
}
function __serialize(): array { function __serialize(): array {
return [$this->dest, $this->repr]; $dest = $this->dest;
if ($dest !== null) $dest = $dest->clone();
return [$dest, $this->repr];
} }
function __unserialize(array $data): void { function __unserialize(array $data): void {
[$this->dest, $this->repr] = $data; [$dest, $this->repr] = $data;
if ($dest !== null) $dest = $dest->clone(true);
$this->dest = $dest;
} }
/** @var MutableDateTime */ protected ?MutableDateTime $dest;
protected $dest;
function getDest(): DateTime { function getDest(): DateTime {
return $this->dest->clone(); $dest = $this->dest;
if ($dest === null) {
$dest = new MutableDateTime();
$dest->add(new \DateInterval(self::INF_INTERVAL));
}
return $dest->clone();
} }
function addDuration($duration) { function addDuration($duration): self {
if ($this->dest !== null) {
if (is_numeric($duration) && $duration < 0) { if (is_numeric($duration) && $duration < 0) {
$this->dest->sub(DateInterval::with(-$duration)); $this->dest->sub(DateInterval::with(-$duration));
} else { } else {
$this->dest->add(DateInterval::with($duration)); $this->dest->add(DateInterval::with($duration));
} }
} }
return $this;
}
function subDuration($duration) { function subDuration($duration): self {
if ($this->dest !== null) {
if (is_numeric($duration) && $duration < 0) { if (is_numeric($duration) && $duration < 0) {
$this->dest->add(DateInterval::with(-$duration)); $this->dest->add(DateInterval::with(-$duration));
} else { } else {
$this->dest->sub(DateInterval::with($duration)); $this->dest->sub(DateInterval::with($duration));
} }
} }
return $this;
}
/** @var string */ /** @var string */
protected $repr; protected $repr;
@ -155,23 +176,20 @@ class Delay {
return $this->repr; return $this->repr;
} }
protected function _getDiff(?DateTimeInterface $now=null): \DateInterval {
$now ??= new \DateTime();
return $this->dest->diff($now);
}
/** retourner true si le délai imparti est écoulé */
function isElapsed(?DateTimeInterface $now=null): bool {
if ($this->repr === "INF") return false;
else return $this->_getDiff($now)->invert == 0;
}
/** /**
* retourner l'intervalle entre le moment courant et la destination. * retourner l'intervalle entre le moment courant et la destination.
* *
* l'intervalle est négatif si le délai n'est pas écoulé, positif sinon * l'intervalle est négatif si le délai n'est pas écoulé, positif sinon
*/ */
function getDiff(?DateTimeInterface $now=null): DateInterval { function getDiff(?DateTimeInterface $now=null): DateInterval {
return new DateInterval($this->_getDiff($now)); $dest = $this->dest;
if ($dest !== null) return $dest->diff($now ?? new \DateTime());
else return new DateInterval("-".self::INF_INTERVAL);
}
/** retourner true si le délai imparti est écoulé */
function isElapsed(?DateTimeInterface $now=null): bool {
if ($this->dest === null) return false;
else return $this->getDiff($now)->invert == 0;
} }
} }

View File

@ -1,10 +1,3 @@
# nulib\php\time # nulib\php\time
* Date, DateTime sont immutables par défaut. par exemple, add() retourne un nouvel objet.
ajouter une version des méthodes qui modifie les données en place en les
préfixant de `_` e.g `_add()`
en terme d'implémentation, dériver \DateTime pour supporter les modification
en place, bien que ce ne soit pas le fonctionnement par défaut
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary -*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary

View File

@ -17,7 +17,7 @@ class F {
} }
/** obtenir le paramètre $name en cherchant dans $_POST puis $_GET */ /** obtenir le paramètre $name en cherchant dans $_POST puis $_GET */
static final function get($name, $default=null, bool $trim=false) { static final function get($name, $default=null, bool $trim=false): ?string {
if ($name === null || $name === false) $value = $default; if ($name === null || $name === false) $value = $default;
elseif (array_key_exists($name, $_POST)) $value = $_POST[$name]; elseif (array_key_exists($name, $_POST)) $value = $_POST[$name];
elseif (array_key_exists($name, $_GET)) $value = $_GET[$name]; elseif (array_key_exists($name, $_GET)) $value = $_GET[$name];
@ -47,6 +47,10 @@ class F {
)); ));
} }
/**
* calculer la liste de tous les paramètres qui ont été passés. ensuite,
* fusionner le tableau $merge s'il est spécifié
*/
static final function merge(?array $merge=null): array { static final function merge(?array $merge=null): array {
$params = []; $params = [];
foreach (self::get_names() as $name) { foreach (self::get_names() as $name) {
@ -56,7 +60,7 @@ class F {
} }
/** /**
* retourner une liste des paramètres qui ont été passés, en les sélectionnant * calculer une liste des paramètres qui ont été passés, en les sélectionnant
* selon le contenu de $includes et $excludes. ensuite, fusionner le tableau * selon le contenu de $includes et $excludes. ensuite, fusionner le tableau
* $merge s'il est spécifié * $merge s'il est spécifié
* *

View File

@ -15,7 +15,7 @@ class G {
} }
/** obtenir le paramètre $name */ /** obtenir le paramètre $name */
static final function get($name, $default=null, bool $trim=false) { static final function get($name, $default=null, bool $trim=false): ?string {
$value = cl::get($_GET, $name, $default); $value = cl::get($_GET, $name, $default);
if ($trim) $value = str::trim($value); if ($trim) $value = str::trim($value);
return $value; return $value;
@ -26,7 +26,11 @@ class G {
$_GET[$name] = $value; $_GET[$name] = $value;
} }
static final function xselect(?array $includes, ?array $excludes=null, ?array $merge=null): array { static final function merge(?array $merge=null): array {
return cl::merge($_GET, $merge);
}
static final function select(?array $includes, ?array $excludes=null, ?array $merge=null): array {
return cl::merge(cl::xselect($_GET, $includes, $excludes), $merge); return cl::merge(cl::xselect($_GET, $includes, $excludes), $merge);
} }
} }

View File

@ -15,7 +15,7 @@ class P {
} }
/** obtenir le paramètre $name */ /** obtenir le paramètre $name */
static final function get($name, $default=null, bool $trim=false) { static final function get($name, $default=null, bool $trim=false): ?string {
$value = cl::get($_POST, $name, $default); $value = cl::get($_POST, $name, $default);
if ($trim) $value = str::trim($value); if ($trim) $value = str::trim($value);
return $value; return $value;
@ -30,4 +30,12 @@ class P {
static final function raw(): string { static final function raw(): string {
return file_get_contents("php://input"); return file_get_contents("php://input");
} }
static final function merge(?array $merge=null): array {
return cl::merge($_POST, $merge);
}
static final function select(?array $includes, ?array $excludes=null, ?array $merge=null): array {
return cl::merge(cl::xselect($_POST, $includes, $excludes), $merge);
}
} }

View File

@ -15,7 +15,7 @@ class R {
} }
/** obtenir le paramètre $name */ /** obtenir le paramètre $name */
static final function get($name, $default=null, bool $trim=false) { static final function get($name, $default=null, bool $trim=false): ?string {
$value = cl::get($_REQUEST, $name, $default); $value = cl::get($_REQUEST, $name, $default);
if ($trim) $value = str::trim($value); if ($trim) $value = str::trim($value);
return $value; return $value;

244
php/src/web/session.php Normal file
View File

@ -0,0 +1,244 @@
<?php
namespace nulib\web;
use nulib\cl;
/**
* Class session: gestion des sessions
*/
class session {
/** durée de session par défaut: 24 minutes */
const DURATION = 1440;
/** @var int */
protected static $duration;
/** obtenir la durée d'une session en secondes. */
static final function get_duration(): int {
$duration = self::$duration;
if ($duration === null) $duration = static::DURATION;
return $duration;
}
/**
* spécifier la durée d'une session en secondes.
* cette méthode doit être appelée avant start()
*/
static final function set_duration(int $duration): void {
self::$duration = $duration;
}
/** vérifier si la session a été démarrée. */
static final function started(): bool {
return session_status() == PHP_SESSION_ACTIVE;
}
private static $started_once = false;
/** vérifier si la session a été démarrée au moins une fois */
static final function started_once(): bool {
return self::$started_once;
}
/** nom de la variable de session qui indique quand elle a été créée. */
const SESSION_CREATION_TIME = "nulib\\session_creation_time";
/**
* démarrer la session si nécessaire. étendre la durée du cookie de session
* de la valeur get_duration()
*
* la page est automatiquement configurée par PHP pour ne pas être mise en
* cache, il est donc inutile d'utiliser {@link http::no_cache()}
*
* retourner true si la session vient d'être créée, false si elle a été
* chargée depuis le disque
*/
static final function start(): bool {
if (!self::started()) {
$duration = self::get_duration();
$canSetCookies = !headers_sent();
if ($canSetCookies) {
# durée initiale du cookie
session_set_cookie_params($duration);
}
session_start([
"gc_maxlifetime" => $duration,
]);
self::$started_once = true;
$creation_time = self::get(self::SESSION_CREATION_TIME, false);
if (!$creation_time) {
# création initiale
self::set(self::SESSION_CREATION_TIME, time());
return true;
} elseif ($canSetCookies) {
# étendre la durée du cookie
$params = session_get_cookie_params();
setcookie(session_name(), session_id(), time() + $duration, $params["path"], $params["domain"], $params["secure"], $params["httponly"]);
}
}
return false;
}
/**
* enregistrer la session, la fermer et libérer son verrou.
*
* cette fonction peut être appelée avant une opération longue si on n'a plus
* besoin de la session.
*
* retourn true si la session a été fermée, false sinon.
*/
static final function close(): bool {
if (self::started()) {
session_write_close();
return true;
}
return false;
}
/**
* vider la session de toutes ses variables ($unsetOnly==true) ou la détruire
* ($unsetOnly==false). en cas de destruction de la session, supprimer aussi
* le cookie de session
*
* si $unsetOnly==true, refaire la variable SESSION_CREATION_TIME
*/
static final function destroy(bool $unsetOnly=false, bool $clearCookie=true): void {
self::start();
if ($unsetOnly) {
session_unset();
self::set(self::SESSION_CREATION_TIME, time());
} else {
$canSetCookies = !headers_sent();
if ($clearCookie && $canSetCookies && ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000, $params["path"], $params["domain"], $params["secure"], $params["httponly"]);
}
session_destroy();
}
}
/**
* Vider la session de toutes les clés spécifiées dans $keys qui ne sont pas
* mentionnées dans $keeps
*
* Si $keys vaut null, toutes les clés sont supprimées comme avec destroy(true)
* notamment, cela signifie que la variable SESSION_CREATION_TIME est refaite
*/
static final function unset_keys(?array $keys, ?array $keeps=null): void {
$updateSessionCreationTime = false;
if ($keys === null) {
$keys = array_keys($_SESSION);
$updateSessionCreationTime = true;
}
if ($keeps !== null) $keys = array_diff($keys, $keeps);
foreach ($keys as $key) {
unset($_SESSION[$key]);
}
if ($updateSessionCreationTime) {
self::set(self::SESSION_CREATION_TIME, time());
}
}
/** vérifier si la session est démarrée et si la clé spécifiée existe. */
static final function has($key): bool {
if ($key === null || $key === false) return false;
return isset($_SESSION) && array_key_exists($key, $_SESSION);
}
/** obtenir la valeur associée à la clé spécifiée si la session est démarrée. */
static final function get(string $key, $default=null) {
if (!isset($_SESSION)) return $default;
return cl::get($_SESSION, $key, $default);
}
/**
* mettre à jour la valeur d'une variable de session.
*
* ne pas chercher à savoir si la session est démarrée ou non
*/
static final function set(string $key, $value): void {
$_SESSION[$key] = $value;
}
/**
* comme {@link set()} mais rouvrir automatiquement la session si nécessaire,
* à condition qu'elle aie déjà été ouverte une fois
*/
static final function setx(string $key, $value): void {
$close = !self::started() && self::started_once();
if ($close) self::start();
self::set($key, $value);
if ($close) self::close();
}
/**
* supprimer une variable de session.
*
* ne pas chercher à savoir si la session est démarrée ou non
*/
static final function del(string $key): void {
unset($_SESSION[$key]);
}
/**
* comme {@link del()} mais rouvrir automatiquement la session si nécessaire,
* à condition qu'elle aie déjà été ouverte une fois
*/
static final function delx(string $key): void {
$close = !self::started() && self::started_once();
if ($close) self::start();
self::del($key);
if ($close) self::close();
}
/** vérifier si chemin de clé spécifié existe dans la session. */
static final function phas($pkey): bool {
return isset($_SESSION) && cl::phas($_SESSION, $pkey);
}
/** obtenir la valeur associée au chemin de clé spécifié si la session est démarrée. */
static final function pget($pkey, $default=null) {
return isset($_SESSION) && cl::pget($_SESSION, $pkey, $default);
}
/**
* mettre à jour la valeur correspondant au chemin de clé spécifié.
*
* ne pas chercher à savoir si la session est démarrée ou non
*/
static final function pset($pkey, $value): void {
cl::pset($_SESSION, $pkey, $value);
}
/**
* comme {@link pset()} mais rouvrir automatiquement la session si nécessaire,
* à condition qu'elle aie déjà été ouverte une fois
*/
static final function psetx($pkey, $value): void {
$close = !self::started() && self::started_once();
if ($close) self::start();
self::pset($pkey, $value);
if ($close) self::close();
}
/**
* supprimer la variable au chemin de clé spécifié.
*
* ne pas chercher à savoir si la session est démarrée ou non
*/
static final function pdel($pkey): void {
cl::pdel($_SESSION, $pkey);
}
/**
* comme {@link pdel()} mais rouvrir automatiquement la session si nécessaire,
* à condition qu'elle aie déjà été ouverte une fois
*/
static final function pdelx(string $key): void {
$close = !self::started() && self::started_once();
if ($close) self::start();
self::pdel($key);
if ($close) self::close();
}
}

1
php/tbin/.gitignore vendored
View File

@ -1,3 +1,4 @@
/devel/
/*.db /*.db
/*.cache /*.cache
/*.log /*.log

7
php/tbin/cachectl.php Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/php
<?php
require __DIR__.'/../vendor/autoload.php';
use cli\CachectlApp;
CachectlApp::run();

14
php/tbin/steam-train.php Executable file
View File

@ -0,0 +1,14 @@
#!/usr/bin/php
<?php
require __DIR__.'/../vendor/autoload.php';
use cli\_SteamTrainApp;
class SteamTrainApp extends _SteamTrainApp {
const PROJDIR = __DIR__;
const VENDOR = [
"autoload" => "../vendor/autoload.php",
"bindir" => "../vendor/bin",
];
}
SteamTrainApp::run();

View File

@ -7,6 +7,15 @@ use nulib\cache\CacheFile;
use nulib\ext\yaml; use nulib\ext\yaml;
use nulib\os\sh; use nulib\os\sh;
function Txx(...$parts) {
$line = [];
foreach ($parts as $part) {
if (is_scalar($part)) $line[] = strval($part);
else $line[] = var_export($part, true);
}
echo implode("", $line)."\n";
}
function show(string $prefix, CacheFile $cache, bool $dumpInfos=true): void { function show(string $prefix, CacheFile $cache, bool $dumpInfos=true): void {
Txx("$prefix=", $cache->get()); Txx("$prefix=", $cache->get());
if ($dumpInfos) { if ($dumpInfos) {

56
php/tbin/test-output1.php Executable file
View File

@ -0,0 +1,56 @@
#!/usr/bin/php
<?php
require __DIR__.'/../vendor/autoload.php';
use nulib\app\cli\Application;
use nulib\output\con;
use nulib\output\IMessenger;
use nulib\output\log;
use nulib\output\say;
use nulib\output\std\LogMessenger;
use nulib\output\std\ProxyMessenger;
Application::run(new class extends Application {
const PROJDIR = __DIR__;
const MSG = 0, CON = 1, LOG = 2;
const ARGS = [
"merge" => parent::ARGS,
["-c", "--con", "name" => "use", "value" => self::CON],
["-l", "--log", "name" => "use", "value" => self::LOG],
["-m", "--msg", "name" => "use", "value" => self::MSG],
];
protected int $use = self::MSG;
function main() {
switch ($this->use) {
case self::MSG:
$msg = new ProxyMessenger();
$msg->addMessenger(con::get());
$msg->addMessenger(new LogMessenger());
break;
case self::CON:
$msg = con::get();
break;
case self::LOG:
$msg = new LogMessenger();
break;
}
$msg->info("test d'information");
$msg->action("attente de 2 secondes", function (IMessenger $msg) {
sleep(1);
$msg->asuccess("1 seconde");
sleep(1);
$msg->asuccess("1 seconde");
});
$msg->action("attente de 2 secondes", function (IMessenger $msg) {
sleep(1);
$msg->info("1 seconde");
sleep(1);
$msg->info("1 seconde");
});
$msg->info("fin de test-appctl");
}
});

View File

@ -1,7 +1,6 @@
<?php <?php
namespace nulib\app\args; namespace nulib\app\args;
use nulib\app\args\SimpleAolist;
use nulib\tests\TestCase; use nulib\tests\TestCase;
class SimpleAolistTest extends TestCase { class SimpleAolistTest extends TestCase {

View File

@ -1,7 +1,6 @@
<?php <?php
namespace nulib\app\args; namespace nulib\app\args;
use nulib\app\args\SimpleArgsParser;
use nulib\tests\TestCase; use nulib\tests\TestCase;
class SimpleArgsParserTest extends TestCase { class SimpleArgsParserTest extends TestCase {

View File

@ -1,7 +1,6 @@
<?php <?php
namespace nulib\cache; namespace nulib\cache;
use nulib\cl;
use nulib\output\msg; use nulib\output\msg;
class cacheTest extends _TestCase { class cacheTest extends _TestCase {

View File

@ -118,4 +118,12 @@ class DateTimeTest extends TestCase {
self::assertFalse($b >= $b2); self::assertFalse($b >= $b2);
self::assertFalse($b >= $b3); self::assertFalse($b >= $b3);
} }
function testSerialize() {
$date = new DateTime();
$serialized = serialize($date);
echo "serialized: $serialized\n";
$unserialized = unserialize($serialized);
self::assertEquals($date, $unserialized);
}
} }

View File

@ -1,7 +1,6 @@
<?php <?php
namespace nulib\php\time; namespace nulib\php\time;
use DateTimeZone;
use nulib\tests\TestCase; use nulib\tests\TestCase;
class DelayTest extends TestCase { class DelayTest extends TestCase {
@ -76,4 +75,27 @@ class DelayTest extends TestCase {
sleep(5); sleep(5);
self::assertTrue($delay->isElapsed()); self::assertTrue($delay->isElapsed());
} }
function testSerialize() {
$delay = new Delay(5);
$serialized = serialize($delay);
echo "serialized: $serialized\n";
$unserialized = unserialize($serialized);
self::assertEquals($delay, $unserialized);
}
function testInf() {
$delay = new Delay("INF");
self::assertSame("INF", strval($delay));
self::assertFalse($delay->isElapsed());
$diff = $delay->getDiff();
self::assertSame("-P1000YT", strval($diff));
$serialized = serialize($delay);
self::assertSame('O:20:"nulib\php\time\Delay":2:{i:0;N;i:1;s:3:"INF";}', $serialized);
echo "serialized: $serialized\n";
$unserialized = unserialize($serialized);
self::assertEquals($delay, $unserialized);
}
} }