modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2023-12-30 18:40:23 +04:00
parent cd7a96f181
commit b97515782b
10 changed files with 186 additions and 19 deletions

View File

@ -2,7 +2,8 @@
namespace nur\sery\sys;
/**
* Interface IContent: un objet capable de produire du contenu à afficher
* Interface IContent: un objet capable de produire du contenu à afficher. le
* contenu retourné doit être pris tel quel,sans plus d'analyse
*/
interface IContent {
/** retourner le contenu à afficher */

View File

@ -0,0 +1,9 @@
<?php
namespace nur\sery\sys;
/**
* Interface IStaticContent: comme {@link IContent} mais la liste retournée est
* une liste de contenu statique à analyser récursivement
*/
interface IStaticContent extends IContent {
}

View File

@ -1,13 +1,29 @@
<?php
namespace nur\sery\sys;
use nulib\cl;
/**
* Class content: gestionnaire de contenu (statique ou dynamique)
*/
class content {
/**
* quoter $vs si c'est un scalaire. sinon retourner le tableau tel quel
*
* NB: cette méthode est accessible via la fonction globale {@link q()}
*/
static final function q($contents): iterable {
if ($contents === null || $contents === false) return [];
elseif ($contents instanceof IContent) return [$contents];
elseif ($contents instanceof IPrintable) return [$contents];
elseif (is_iterable($contents)) return $contents;
else return [htmlspecialchars(strval($contents))];
}
/**
* applatir $contents, et retourner une liste de valeurs scalaires:
* - les instances de IContent et IPrintable sont remplacées par leur contenu
* - les chaines sont ajoutées telles quelles
* - les autres objets sont transformés en chaine de caractère avec strval()
*/
static function flatten($contents, ?array &$dest=null): array {
@ -20,12 +36,18 @@ class content {
$contents->print();
$dest[] = ob_get_clean();
return $dest;
} elseif (!is_iterable($contents)) {
} elseif (is_scalar($contents)) {
$dest[] = $contents;
return $dest;
} elseif (!is_iterable($contents)) {
$dest[] = strval($contents);
return $dest;
}
# sinon parcourir $contents
foreach ($contents as $value) {
self::flatten($value, $dest);
if ($value === null) continue;
elseif (is_scalar($value)) $dest[] = $value;
else self::flatten($value, $dest);
}
return $dest;
}
@ -41,6 +63,10 @@ class content {
if ($quote) $contents = htmlspecialchars($contents);
$dest[] = $contents;
return $dest;
} elseif ($contents instanceof IStaticContent) {
$contents = $contents->getContent();
$dest = cl::merge($dest, self::resolve_static($contents, $dest_class, false));
return $dest;
} elseif ($contents instanceof IContent) {
$dest[] = $contents;
return $dest;
@ -53,8 +79,10 @@ class content {
}
foreach ($contents as $content) {
if ($content === null) continue;
elseif (is_string($content)) $dest[] = $content;
elseif ($contents instanceof IContent) $dest[] = $content;
elseif ($contents instanceof IStaticContent) {
$contents = $content->getContent();
$dest = cl::merge($dest, self::resolve_static($contents, $dest_class, false));
} elseif ($contents instanceof IContent) $dest[] = $content;
elseif ($contents instanceof IPrintable) $dest[] = $content;
elseif (!is_array($content)) $dest[] = $content;
else {
@ -67,13 +95,15 @@ class content {
# - ou une définition d'objet d'une des formes suivantes
# - [class, ...args]
# - [[class], ...args]
$class = false;
$func_isa_class = false;
if (array_key_exists(0, $content) && is_array($content[0])) {
# $content est de la forme [func_or_class, ...args]
$func = $content[0];
$args = array_slice($content, 1);
if (func::is_class($func)) {
$class = true;
if (func::is_static($func)) {
func::ensure_func($func, $dest_class, $args);
} elseif (func::is_class($func)) {
$func_isa_class = true;
func::fix_class_args($func, $args);
} else {
func::ensure_func($func, $dest_class, $args);
@ -81,8 +111,10 @@ class content {
} else {
# $content est de la forme $func
$func = $content;
if (func::is_class($func)) {
$class = true;
if (func::is_static($func)) {
func::ensure_func($func, $dest_class, $args);
} elseif (func::is_class($func)) {
$func_isa_class = true;
func::fix_class_args($func, $args);
} else {
func::ensure_func($func, $dest_class, $args);
@ -90,19 +122,29 @@ class content {
}
# chaque argument est un contenu statique
foreach ($args as &$arg) {
$array = is_array($arg);
$arg = self::resolve_static($arg, $dest_class, false);
$arg = self::flatten($arg);
switch (count($arg)) {
case 0: $arg = null; break;
case 1: $arg = $arg[0]; break;
default: $arg = implode("", $arg);
if ($array) {
$arg = [implode("", $arg)];
} else {
switch (count($arg)) {
case 0: $arg = null; break;
case 1: $arg = $arg[0]; break;
default: $arg = implode("", $arg);
}
}
}; unset($arg);
# puis appeler la fonction
if ($class) $content = func::cons($func, ...$args);
if ($func_isa_class) $content = func::cons($func, ...$args);
else $content = func::call($func, ...$args);
$dest[] = $content;
}
}
return $dest;
}
static function to_string($contents, $dest_class=null, bool $quote=true): string {
return implode("", self::flatten(self::resolve_static($contents, $dest_class, $quote)));
}
}

View File

@ -18,8 +18,11 @@ class func {
* tester si $func est une chaine de la forme "XXX::method" XXX est une
* chaine quelconque éventuellement vide, ou un tableau de la forme ["method"]
* ou [anything, "method", ...]
*
* Avec la forme tableau, "method" ne doit pas contenir le caractère '\', pour
* pouvoir utiliser conjointement {@link is_class()}
*/
static final function is_static($func): bool {
static final function is_static($func, bool $allowClass=false): bool {
if (is_string($func)) {
$pos = strpos($func, "::");
if ($pos === false) return false;
@ -27,10 +30,14 @@ class func {
} elseif (is_array($func) && array_key_exists(0, $func)) {
$count = count($func);
if ($count == 1) {
return is_string($func[0]) && strlen($func[0]) > 0;
if (!is_string($func[0]) || strlen($func[0]) == 0) return false;
if (strpos($func[0], "\\") !== false) return false;
return true;
} elseif ($count > 1) {
if (!array_key_exists(1, $func)) return false;
return is_string($func[1]) && strlen($func[1]) > 0;
if (!is_string($func[1]) || strlen($func[1]) == 0) return false;
if (strpos($func[1], "\\") !== false) return false;
return true;
}
}
return false;
@ -351,7 +358,8 @@ class func {
*
* NB: il est possible d'avoir {@link is_static()} et {@link is_class()}
* vraies pour la même valeur. s'il faut supporter les deux cas, appeler
* {@link is_class()} d'abord
* {@link is_static()} d'abord, mais dans ce cas, on ne supporte que les
* classes qui sont dans un package
*/
static final function is_class($class): bool {
if (is_string($class)) {

38
tests/sys/contentTest.php Normal file
View File

@ -0,0 +1,38 @@
<?php
namespace nur\sery\sys;
use nur\sery\sys\impl\html;
use PHPUnit\Framework\TestCase;
class contentTest extends TestCase {
function testFlattern() {
self::assertSame([], content::flatten(null));
self::assertSame([], content::flatten([null]));
self::assertSame([""], content::flatten(""));
self::assertSame([""], content::flatten([""]));
self::assertSame(["a", "b", "c", "d"], content::flatten(["a", ["b", ["c"], "d"]]));
}
function testResolve_static() {
self::assertSame([], content::resolve_static(null, null));
self::assertSame([], content::resolve_static([null], [null]));
self::assertSame([""], content::resolve_static("", ""));
self::assertSame([""], content::resolve_static([""], [""]));
self::assertSame(["&lt;quoted&gt;"], content::resolve_static("<quoted>", "<quoted>"));
self::assertSame(["<non-quoted>"], content::resolve_static(["<non-quoted>"], ["<non-quoted>"]));
self::assertSame(
"<h1>title&lt;q/&gt;</h1><p>hello<nq/><span>brave&lt;q/&gt;</span><span>world<nq/></span></p>",
content::to_string([
[html::H1, "title<q/>"],
[html::P, [
"hello<nq/>",
[html::SPAN, "brave<q/>"],
[html::SPAN, ["world<nq/>"]],
]],
]));
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace nur\sery\sys\impl;
use nur\sery\sys\IContent;
class AContent implements IContent {
function getContent(): iterable {
return ["<span>content</span>"];
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace nur\sery\sys\impl;
use nur\sery\sys\IPrintable;
class APrintable implements IPrintable {
function print(): void {
echo "<p>printable</p>";
}
}

View File

@ -0,0 +1,12 @@
<?php
namespace nur\sery\sys\impl;
use nur\sery\sys\IStaticContent;
class AStaticContent implements IStaticContent {
function getContent(): iterable {
return [
[html::P, "static content"],
];
}
}

23
tests/sys/impl/ATag.php Normal file
View File

@ -0,0 +1,23 @@
<?php
namespace nur\sery\sys\impl;
use nur\sery\sys\content;
use nur\sery\sys\IStaticContent;
class ATag implements IStaticContent {
function __construct(string $tag, $contents=null) {
$this->tag = $tag;
$this->contents = $contents;
}
protected $tag;
protected $contents;
function getContent(): iterable {
return [
"<$this->tag>",
...content::q($this->contents),
"</$this->tag>",
];
}
}

14
tests/sys/impl/html.php Normal file
View File

@ -0,0 +1,14 @@
<?php
namespace nur\sery\sys\impl;
class html {
const H1 = [self::class, "h1"];
const DIV = [self::class, "div"];
const P = [self::class, "p"];
const SPAN = [self::class, "span"];
static function h1($contents) { return new ATag("h1", $contents); }
static function div($contents) { return new ATag("div", $contents); }
static function p($contents) { return new ATag("p", $contents); }
static function span($contents) { return new ATag("span", $contents); }
}