diff --git a/php/src/cl.php b/php/src/cl.php index 2397fe9..c0e64e8 100644 --- a/php/src/cl.php +++ b/php/src/cl.php @@ -2,6 +2,7 @@ namespace nulib; use ArrayAccess; +use nulib\php\func; use nulib\php\nur_func; use Traversable; @@ -348,12 +349,12 @@ class cl { ############################################################################# - static final function map(callable $callback, ?iterable $array): array { + static final function map($func, ?iterable $array): array { $result = []; if ($array !== null) { - $ctx = nur_func::_prepare($callback); + $func = func::with($func); foreach ($array as $key => $value) { - $result[$key] = nur_func::_call($ctx, [$value, $key]); + $result[$key] = $func->invoke([$value, $key]); } } return $result; diff --git a/php/src/db/Capacitor.php b/php/src/db/Capacitor.php index 90c3c9b..70c6f46 100644 --- a/php/src/db/Capacitor.php +++ b/php/src/db/Capacitor.php @@ -1,7 +1,7 @@ commit(); $commited = true; diff --git a/php/src/db/CapacitorStorage.php b/php/src/db/CapacitorStorage.php index ec27fae..c5cf345 100644 --- a/php/src/db/CapacitorStorage.php +++ b/php/src/db/CapacitorStorage.php @@ -3,7 +3,7 @@ namespace nulib\db; use nulib\cl; use nulib\db\cache\cache; -use nulib\php\nur_func; +use nulib\php\func; use nulib\ValueException; use Traversable; @@ -230,10 +230,7 @@ EOT; $db = $this->db(); $args ??= []; - $initFunc = [$channel, "getItemValues"]; - $initArgs = $args; - nur_func::ensure_func($initFunc, null, $initArgs); - $values = nur_func::call($initFunc, $item, ...$initArgs); + $values = func::call([$channel, "getItemValues"], $item, ...$args); if ($values === [false]) return 0; $row = cl::merge( @@ -259,9 +256,7 @@ EOT; "modified_" => $now, ]); $insert = true; - $initFunc = [$channel, "onCreate"]; - $initArgs = $args; - nur_func::ensure_func($initFunc, null, $initArgs); + $initFunc = func::with([$channel, "onCreate"], $args); $values = $this->unserialize($channel, $row); $pvalues = null; } else { @@ -276,14 +271,12 @@ EOT; } else { $row = cl::merge($prow, $row); } - $initFunc = [$channel, "onUpdate"]; - $initArgs = $args; - nur_func::ensure_func($initFunc, null, $initArgs); + $initFunc = func::with([$channel, "onUpdate"], $args); $values = $this->unserialize($channel, $row); $pvalues = $this->unserialize($channel, $prow); } - $updates = nur_func::call($initFunc, $item, $values, $pvalues, ...$initArgs); + $updates = $initFunc->prependArgs(null, [$item, $values, $pvalues])->invoke(); if ($updates === [false]) return 0; if (is_array($updates) && $updates) { if ($insert === null) $insert = false; @@ -295,8 +288,10 @@ EOT; } if ($func !== null) { - nur_func::ensure_func($func, $channel, $args); - $updates = nur_func::call($func, $item, $values, $pvalues, ...$args); + $updates = func::with($func) + ->prependArgs(null, [$item, $values, $pvalues]) + ->bind($channel, true) + ->invoke(); if ($updates === [false]) return 0; if (is_array($updates) && $updates) { if ($insert === null) $insert = false; @@ -510,8 +505,7 @@ EOT; function _each(CapacitorChannel $channel, $filter, $func, ?array $args, ?array $mergeQuery=null, ?int &$nbUpdated=null): int { $this->_create($channel); if ($func === null) $func = CapacitorChannel::onEach; - nur_func::ensure_func($func, $channel, $args); - $onEach = nur_func::_prepare($func); + $onEach = func::with($func)->bind($channel, true); $db = $this->db(); # si on est déjà dans une transaction, désactiver la gestion des transactions $manageTransactions = $channel->isManageTransactions() && !$db->inTransaction(); @@ -528,7 +522,7 @@ EOT; $all = $this->_allCached("each", $channel, $filter, $mergeQuery); foreach ($all as $values) { $rowIds = $this->getRowIds($channel, $values); - $updates = nur_func::_call($onEach, [$values["item"], $values, ...$args]); + $updates = $onEach->invoke([$values["item"], $values, ...$args]); if (is_array($updates) && $updates) { if (!array_key_exists("modified_", $updates)) { $updates["modified_"] = date("Y-m-d H:i:s"); @@ -579,8 +573,7 @@ EOT; function _delete(CapacitorChannel $channel, $filter, $func, ?array $args): int { $this->_create($channel); if ($func === null) $func = CapacitorChannel::onDelete; - nur_func::ensure_func($func, $channel, $args); - $onEach = nur_func::_prepare($func); + $onEach = func::with($func)->bind($channel, true); $db = $this->db(); # si on est déjà dans une transaction, désactiver la gestion des transactions $manageTransactions = $channel->isManageTransactions() && !$db->inTransaction(); @@ -596,7 +589,7 @@ EOT; $all = $this->_allCached("delete", $channel, $filter); foreach ($all as $values) { $rowIds = $this->getRowIds($channel, $values); - $delete = boolval(nur_func::_call($onEach, [$values["item"], $values, ...$args])); + $delete = boolval($onEach->invoke([$values["item"], $values, ...$args])); if ($delete) { $db->exec([ "delete", diff --git a/php/src/db/pdo/Pdo.php b/php/src/db/pdo/Pdo.php index b2b24c9..2fa8bf0 100644 --- a/php/src/db/pdo/Pdo.php +++ b/php/src/db/pdo/Pdo.php @@ -6,7 +6,7 @@ use nulib\cl; use nulib\db\_private\Tvalues; use nulib\db\IDatabase; use nulib\db\ITransactor; -use nulib\php\nur_func; +use nulib\php\func; use nulib\ValueException; class Pdo implements IDatabase { @@ -119,8 +119,7 @@ class Pdo implements IDatabase { $dbconn = $this->dbconn; $options = $this->options; if (is_callable($options)) { - nur_func::ensure_func($options, $this, $args); - $options = nur_func::call($options, ...$args); + $options = func::with($options)->bind($this, true)->invoke(); } $this->db = new \PDO($dbconn["name"], $dbconn["user"], $dbconn["pass"], $options); _config::with($this->config)->configure($this); @@ -191,7 +190,7 @@ class Pdo implements IDatabase { if ($func !== null) { $commited = false; try { - nur_func::call($func, $this); + func::call($func, $this); if ($commit) { $this->commit(); $commited = true; diff --git a/php/src/db/pdo/_config.php b/php/src/db/pdo/_config.php index 5055d6f..2cc9c96 100644 --- a/php/src/db/pdo/_config.php +++ b/php/src/db/pdo/_config.php @@ -1,7 +1,7 @@ configs as $key => $config) { - if (is_string($config) && !nur_func::is_method($config)) { + if (is_string($config) && !func::is_method($config)) { $pdo->exec($config); } else { - nur_func::ensure_func($config, $this, $args); - nur_func::call($config, $pdo, $key, ...$args); + func::with($config)->bind($this, true)->invoke([$pdo, $key]); } } } diff --git a/php/src/db/sqlite/Sqlite.php b/php/src/db/sqlite/Sqlite.php index cf14dcd..89db30b 100644 --- a/php/src/db/sqlite/Sqlite.php +++ b/php/src/db/sqlite/Sqlite.php @@ -6,7 +6,7 @@ use nulib\cl; use nulib\db\_private\Tvalues; use nulib\db\IDatabase; use nulib\db\ITransactor; -use nulib\php\nur_func; +use nulib\php\func; use nulib\ValueException; use SQLite3; use SQLite3Result; @@ -237,7 +237,7 @@ class Sqlite implements IDatabase { if ($func !== null) { $commited = false; try { - nur_func::call($func, $this); + func::call($func, $this); if ($commit) { $this->commit(); $commited = true; diff --git a/php/src/db/sqlite/_config.php b/php/src/db/sqlite/_config.php index ea7553a..bcfe8fb 100644 --- a/php/src/db/sqlite/_config.php +++ b/php/src/db/sqlite/_config.php @@ -1,7 +1,7 @@ configs as $key => $config) { - if (is_string($config) && !nur_func::is_method($config)) { + if (is_string($config) && !func::is_method($config)) { $sqlite->exec($config); } else { - nur_func::ensure_func($config, $this, $args); - nur_func::call($config, $sqlite, $key, ...$args); + func::with($config)->bind($this, true)->invoke([$sqlite, $key]); } } } diff --git a/php/src/db/sqlite/_migration.php b/php/src/db/sqlite/_migration.php index d2adf93..b5ee6f4 100644 --- a/php/src/db/sqlite/_migration.php +++ b/php/src/db/sqlite/_migration.php @@ -1,6 +1,7 @@ $migration, "done" => 0, ]); - if (is_string($migration) && !nur_func::is_method($migration)) { + if (is_string($migration) && !func::is_method($migration)) { $sqlite->exec($migration); } else { - nur_func::ensure_func($migration, $this, $args); - nur_func::call($migration, $sqlite, $key, ...$args); + func::with($migration)->bind($this, true)->invoke([$sqlite, $key]); } $sqlite->exec("update _migration set done = 1 where key = :key", [ "key" => $key, diff --git a/php/src/file/tab/AbstractBuilder.php b/php/src/file/tab/AbstractBuilder.php index f1ec869..77d61b7 100644 --- a/php/src/file/tab/AbstractBuilder.php +++ b/php/src/file/tab/AbstractBuilder.php @@ -5,6 +5,7 @@ use DateTimeInterface; use nulib\cl; use nulib\file\TempStream; use nulib\os\path; +use nulib\php\func; use nulib\php\nur_func; use nulib\php\time\DateTime; use nulib\web\http; @@ -35,13 +36,8 @@ abstract class AbstractBuilder extends TempStream implements IBuilder { $this->rows = $rows; $this->index = 0; $cookFunc = $params["cook_func"] ?? null; - $cookCtx = $cookArgs = null; - if ($cookFunc !== null) { - nur_func::ensure_func($cookFunc, $this, $cookArgs); - $cookCtx = nur_func::_prepare($cookFunc); - } - $this->cookCtx = $cookCtx; - $this->cookArgs = $cookArgs; + if ($cookFunc !== null) $cookFunc = func::with($cookFunc)->bind($this, true); + $this->cookFunc = $cookFunc; $this->output = $params["output"] ?? static::OUTPUT; $maxMemory = $params["max_memory"] ?? null; $throwOnError = $params["throw_on_error"] ?? null; @@ -60,9 +56,7 @@ abstract class AbstractBuilder extends TempStream implements IBuilder { protected ?string $output; - protected ?array $cookCtx; - - protected ?array $cookArgs; + protected ?func $cookFunc; protected function ensureHeaders(?array $row=null): void { if ($this->headers !== null || !$this->useHeaders) return; @@ -87,9 +81,8 @@ abstract class AbstractBuilder extends TempStream implements IBuilder { } protected function cookRow(?array $row): ?array { - if ($this->cookCtx !== null) { - $args = cl::merge([$row], $this->cookArgs); - $row = nur_func::_call($this->cookCtx, $args); + if ($this->cookFunc !== null) { + $row = $this->cookFunc->prependArgs(null, [$row])->invoke(); } if ($row !== null) { foreach ($row as &$col) { diff --git a/php/src/output/msg.php b/php/src/output/msg.php index 3576127..d180d18 100644 --- a/php/src/output/msg.php +++ b/php/src/output/msg.php @@ -2,7 +2,7 @@ namespace nulib\output; use nulib\output\std\ProxyMessenger; -use nulib\php\nur_func; +use nulib\php\func; /** * Class msg: inscrire un message dans les logs ET l'afficher à l'utilisateur @@ -39,30 +39,21 @@ class msg extends _messenger { if ($log !== null && $log !== false) { if ($log instanceof IMessenger) log::set_messenger($log); elseif (is_string($log)) log::set_messenger_class($log); - elseif (is_array($log)) { - nur_func::ensure_class($log, $args); - $log = nur_func::cons($log, $args); - } + else $log = func::call($log); log::set_messenger($log); $msgs[] = $log; } if ($console !== null && $console !== false) { if ($console instanceof IMessenger) console::set_messenger($console); elseif (is_string($console)) console::set_messenger_class($console); - elseif (is_array($console)) { - nur_func::ensure_class($console, $args); - $console = nur_func::cons($console, $args); - } + else $console = func::call($console); console::set_messenger($console); $msgs[] = $console; } if ($say !== null && $say !== false) { if ($say instanceof IMessenger) say::set_messenger($say); elseif (is_string($say)) say::set_messenger_class($say); - elseif (is_array($say)) { - nur_func::ensure_class($say, $args); - $say = nur_func::cons($say, $args); - } + else $say = func::call($say); say::set_messenger($say); $msgs[] = $say; } diff --git a/php/src/php/content/c.php b/php/src/php/content/c.php index 9506835..4105846 100644 --- a/php/src/php/content/c.php +++ b/php/src/php/content/c.php @@ -3,7 +3,7 @@ namespace nulib\php\content; use Closure; use nulib\cl; -use nulib\php\nur_func; +use nulib\php\func; /** * Class c: classe outil pour gérer du contenu @@ -62,8 +62,7 @@ class c { # contenu dynamique: le contenu est la valeur de retour de la fonction # ce contenu est rajouté à la suite après avoir été quoté avec self::q() $func = $value; - nur_func::ensure_func($func, $object_or_class, $args); - $values = self::q(nur_func::call($func, ...$args)); + $values = self::q(func::call($func)); self::add_static_content($dest, $values, $key, $seq); continue; } @@ -83,16 +82,7 @@ class c { $arg = self::resolve($arg, $object_or_class, false); if (!$array) $arg = $arg[0]; }; unset($arg); - if (nur_func::is_static($func)) { - nur_func::ensure_func($func, $object_or_class, $args); - $value = nur_func::call($func, ...$args); - } elseif (nur_func::is_class($func)) { - nur_func::fix_class_args($func, $args); - $value = nur_func::cons($func, ...$args); - } else { - nur_func::ensure_func($func, $object_or_class, $args); - $value = nur_func::call($func, ...$args); - } + $value = func::with($func, $args)->bind($object_or_class, true)->invoke(); } } if ($seq) $dest[] = $value; diff --git a/php/src/php/func.php b/php/src/php/func.php index 133fd5f..d81b416 100644 --- a/php/src/php/func.php +++ b/php/src/php/func.php @@ -592,6 +592,27 @@ class func { protected int $maxArgs; + function replaceArgs(?array $args): self { + $this->prefixArgs = $args?? []; + return $this; + } + + function prependArgs(?int $stripCount=null, ?array $args=null): self { + if ($stripCount !== null || $args !== null) { + array_splice($this->prefixArgs, 0, $stripCount ?? 0, $args); + } + return $this; + } + + function appendArgs(?int $stripCount=null, ?array $args=null): self { + if ($stripCount !== null || $args !== null) { + $stripCount ??= 0; + if ($stripCount > 0) array_splice($this->prefixArgs, -$stripCount); + $this->prefixArgs = array_merge($this->prefixArgs, $args); + } + return $this; + } + protected function updateReflection($reflection): void { $variadic = false; $minArgs = $maxArgs = 0; @@ -627,11 +648,16 @@ class func { else return $this->bound && $this->object !== null; } - function bind($object): self { + function bind($object, bool $unlessAlreadyBound=false, bool $replace=false): self { if ($this->type !== self::TYPE_METHOD) return $this; + if ($this->object !== null && $unlessAlreadyBound) return $this; [$c, $f] = $this->func; - if ($this->reflection === null) { + if ($replace) { + $c = $object; + $this->func = [$c, $f]; + $this->updateReflection(new ReflectionMethod($c, $f)); + } elseif ($this->reflection === null) { $this->func[0] = $c = $object; $this->updateReflection(new ReflectionMethod($c, $f)); } diff --git a/php/src/php/mprop.php b/php/src/php/mprop.php index a0bc28d..b036844 100644 --- a/php/src/php/mprop.php +++ b/php/src/php/mprop.php @@ -44,7 +44,7 @@ class mprop { } catch (ReflectionException $e) { return oprop::get($object, $property, $default); } - return nur_func::call([$object, $m], $default); + return func::call([$object, $m], $default); } /** spécifier la valeur d'une propriété */ @@ -60,7 +60,7 @@ class mprop { } catch (ReflectionException $e) { return oprop::_set($c, $object, $property, $value); } - nur_func::call([$object, $m], $value); + func::call([$object, $m], $value); return $value; } diff --git a/php/src/php/nur_func.php b/php/src/php/nur_func.php deleted file mode 100644 index ba4cc06..0000000 --- a/php/src/php/nur_func.php +++ /dev/null @@ -1,453 +0,0 @@ - 1) { - if (!array_key_exists(1, $func)) return false; - if (!is_string($func[1]) || strlen($func[1]) == 0) return false; - if (strpos($func[1], "\\") !== false) return false; - return true; - } - } - return false; - } - - /** - * si $func est une chaine de la forme "::method" alors la remplacer par la - * chaine "$class::method" - * - * si $func est un tableau de la forme ["method"] ou [null, "method"], alors - * le remplacer par [$class, "method"] - * - * on assume que {@link is_static()}($func) retourne true - * - * @return bool true si la correction a été faite - */ - static final function fix_static(&$func, $class): bool { - if (is_object($class)) $class = get_class($class); - - if (is_string($func) && substr($func, 0, 2) == "::") { - $func = "$class$func"; - return true; - } elseif (is_array($func) && array_key_exists(0, $func)) { - $count = count($func); - if ($count == 1) { - $func = [$class, $func[0]]; - return true; - } elseif ($count > 1 && $func[0] === null) { - $func[0] = $class; - return true; - } - } - return false; - } - - /** tester si $method est une chaine de la forme "->method" */ - private static function isam($method): bool { - return is_string($method) - && strlen($method) > 2 - && substr($method, 0, 2) == "->"; - } - - /** - * tester si $func est une chaine de la forme "->method" ou un tableau de la - * forme ["->method", ...] ou [anything, "->method", ...] - */ - static final function is_method($func): bool { - if (is_string($func)) { - return self::isam($func); - } elseif (is_array($func) && array_key_exists(0, $func)) { - if (self::isam($func[0])) { - # ["->method", ...] - return true; - } - if (array_key_exists(1, $func) && self::isam($func[1])) { - # [anything, "->method", ...] - return true; - } - } - return false; - } - - /** - * si $func est une chaine de la forme "->method" alors la remplacer par le - * tableau [$object, "method"] - * - * si $func est un tableau de la forme ["->method"] ou [anything, "->method"], - * alors le remplacer par [$object, "method"] - * - * @return bool true si la correction a été faite - */ - static final function fix_method(&$func, $object): bool { - if (!is_object($object)) return false; - - if (is_string($func)) { - if (self::isam($func)) { - $func = [$object, substr($func, 2)]; - return true; - } - } elseif (is_array($func) && array_key_exists(0, $func)) { - if (self::isam($func[0])) $func = array_merge([null], $func); - if (count($func) > 1 && array_key_exists(1, $func) && self::isam($func[1])) { - $func[0] = $object; - $func[1] = substr($func[1], 2); - return true; - } - } - return false; - } - - /** - * si $func est un tableau de plus de 2 éléments, alors déplacer les éléments - * supplémentaires au début de $args. par exemple: - * ~~~ - * $func = ["class", "method", "arg1", "arg2"]; - * $args = ["arg3"]; - * func::fix_args($func, $args) - * # $func === ["class", "method"] - * # $args === ["arg1", "arg2", "arg3"] - * ~~~ - * - * @return bool true si la correction a été faite - */ - static final function fix_args(&$func, ?array &$args): bool { - if ($args === null) $args = []; - if (is_array($func) && count($func) > 2) { - $prefix_args = array_slice($func, 2); - $func = array_slice($func, 0, 2); - $args = array_merge($prefix_args, $args); - return true; - } - return false; - } - - /** - * s'assurer que $func est un appel de méthode ou d'une méthode statique; - * et renseigner le cas échéant les arguments. si $func ne fait pas mention - * de la classe ou de l'objet, le renseigner avec $class_or_object. - * - * @return bool true si c'est une fonction valide. il ne reste plus qu'à - * l'appeler avec {@link call()} - */ - static final function check_func(&$func, $class_or_object, &$args=null): bool { - if ($func instanceof Closure) return true; - if (self::is_method($func)) { - # méthode - self::fix_method($func, $class_or_object); - self::fix_args($func, $args); - return true; - } elseif (self::is_static($func)) { - # méthode statique - self::fix_static($func, $class_or_object); - self::fix_args($func, $args); - return true; - } - return false; - } - - /** - * Comme {@link check_func()} mais lance une exception si la fonction est - * invalide - * - * @throws ValueException si $func n'est pas une fonction ou une méthode valide - */ - static final function ensure_func(&$func, $class_or_object, &$args=null): void { - if (!self::check_func($func, $class_or_object, $args)) { - throw ValueException::invalid_type($func, "callable"); - } - } - - static final function _prepare($func): array { - $object = null; - if (is_callable($func)) { - if (is_array($func)) { - $rf = new ReflectionMethod(...$func); - $object = $func[0]; - if (is_string($object)) $object = null; - } elseif ($func instanceof Closure) { - $rf = new ReflectionFunction($func); - } elseif (is_string($func) && strpos($func, "::") === false) { - $rf = new ReflectionFunction($func); - } else { - $rf = new ReflectionMethod($func); - } - } elseif ($func instanceof ReflectionMethod) { - $rf = $func; - } elseif ($func instanceof ReflectionFunction) { - $rf = $func; - } elseif (is_array($func) && count($func) == 2 && isset($func[0]) && isset($func[1]) - && ($func[1] instanceof ReflectionMethod || $func[1] instanceof ReflectionFunction)) { - $object = $func[0]; - if (is_string($object)) $object = null; - $rf = $func[1]; - } elseif (is_string($func) && strpos($func, "::") === false) { - $rf = new ReflectionFunction($func); - } else { - throw ValueException::invalid_type($func, "callable"); - } - $minArgs = $rf->getNumberOfRequiredParameters(); - $maxArgs = $rf->getNumberOfParameters(); - $variadic = $rf->isVariadic(); - return [$rf instanceof ReflectionMethod, $object, $rf, $minArgs, $maxArgs, $variadic]; - } - - static final function _fill(array $context, array &$args): void { - $minArgs = $context[3]; - $maxArgs = $context[4]; - $variadic = $context[5]; - if (!$variadic) $args = array_slice($args, 0, $maxArgs); - while (count($args) < $minArgs) $args[] = null; - } - - static final function _call($context, array $args) { - self::_fill($context, $args); - $use_object = $context[0]; - $object = $context[1]; - $method = $context[2]; - if ($use_object) { - if (count($args) === 0) return $method->invoke($object); - else return $method->invokeArgs($object, $args); - } else { - if (count($args) === 0) return $method->invoke(); - else return $method->invokeArgs($args); - } - } - - /** - * Appeler la fonction spécifiée avec les arguments spécifiés. - * Adapter $args en fonction du nombre réel d'arguments de $func - * - * @param callable|ReflectionFunction|ReflectionMethod $func - */ - static final function call($func, ...$args) { - return self::_call(self::_prepare($func), $args); - } - - /** remplacer $value par $func($value, ...$args) */ - static final function apply(&$value, $func, ...$args): void { - if ($func !== null) { - if ($args) $args = array_merge([$value], $args); - else $args = [$value]; - $value = self::call($func, ...$args); - } - } - - const MASK_PS = ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_STATIC; - const MASK_P = ReflectionMethod::IS_PUBLIC; - const METHOD_PS = ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_STATIC; - const METHOD_P = ReflectionMethod::IS_PUBLIC; - - private static function matches(string $name, array $includes, array $excludes): bool { - if ($includes) { - $matches = false; - foreach ($includes as $include) { - if (substr($include, 0, 1) == "/") { - # expression régulière - if (preg_match($include, $name)) { - $matches = true; - break; - } - } else { - # tester la présence de la sous-chaine - if (strpos($name, $include) !== false) { - $matches = true; - break; - } - } - } - if (!$matches) return false; - } - foreach ($excludes as $exclude) { - if (substr($exclude, 0, 1) == "/") { - # expression régulière - if (preg_match($exclude, $name)) return false; - } else { - # tester la présence de la sous-chaine - if (strpos($name, $exclude) !== false) return false; - } - } - return true; - } - - /** @var Schema */ - private static $call_all_params_schema; - - /** - * retourner la liste des méthodes de $class_or_object qui correspondent au - * filtre $options. le filtre doit respecter le schéme {@link CALL_ALL_PARAMS_SCHEMA} - */ - static function get_all($class_or_object, $params=null): array { - Schema::nv($paramsv, $params, null - , self::$call_all_params_schema, ref_func::CALL_ALL_PARAMS_SCHEMA); - if (is_callable($class_or_object, true) && is_array($class_or_object)) { - # callable sous forme de tableau - $class_or_object = $class_or_object[0]; - } - if (is_string($class_or_object)) { - # lister les méthodes publiques statiques de la classe - $mask = self::MASK_PS; - $expected = self::METHOD_PS; - $c = new ReflectionClass($class_or_object); - } elseif (is_object($class_or_object)) { - # lister les méthodes publiques de la classe - $c = new ReflectionClass($class_or_object); - $mask = $params["static_only"]? self::MASK_PS: self::MASK_P; - $expected = $params["static_only"]? self::METHOD_PS: self::METHOD_P; - } else { - throw new ValueException("$class_or_object: vous devez spécifier une classe ou un objet"); - } - $prefix = $params["prefix"]; $prefixlen = strlen($prefix); - $args = $params["args"]; - $includes = $params["include"]; - $excludes = $params["exclude"]; - $methods = []; - foreach ($c->getMethods() as $m) { - if (($m->getModifiers() & $mask) != $expected) continue; - $name = $m->getName(); - if (substr($name, 0, $prefixlen) != $prefix) continue; - if (!self::matches($name, $includes, $excludes)) continue; - $methods[] = cl::merge([$class_or_object, $name], $args); - } - return $methods; - } - - /** - * Appeler toutes les méthodes publiques de $object_or_class et retourner un - * tableau [$method_name => $return_value] des valeurs de retour. - */ - static final function call_all($class_or_object, $params=null): array { - $methods = self::get_all($class_or_object, $params); - $values = []; - foreach ($methods as $method) { - self::fix_args($method, $args); - $values[$method[1]] = self::call($method, ...$args); - } - return $values; - } - - /** - * tester si $func est une chaine de la forme "XXX" où XXX est une classe - * valide, ou un tableau de la forme ["XXX", ...] - * - * 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_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)) { - return class_exists($class); - } elseif (is_array($class) && array_key_exists(0, $class)) { - return class_exists($class[0]); - } - return false; - } - - /** - * en assumant que {@link is_class()} est vrai, si $class est un tableau de - * plus de 1 éléments, alors déplacer les éléments supplémentaires au début de - * $args. par exemple: - * ~~~ - * $class = ["class", "arg1", "arg2"]; - * $args = ["arg3"]; - * func::fix_class_args($class, $args) - * # $class === "class" - * # $args === ["arg1", "arg2", "arg3"] - * ~~~ - * - * @return bool true si la correction a été faite - */ - static final function fix_class_args(&$class, ?array &$args): bool { - if ($args === null) $args = []; - if (is_array($class)) { - if (count($class) > 1) { - $prefix_args = array_slice($class, 1); - $class = array_slice($class, 0, 1)[0]; - $args = array_merge($prefix_args, $args); - } else { - $class = $class[0]; - } - return true; - } - return false; - } - - /** - * s'assurer que $class est une classe et renseigner le cas échéant les - * arguments. - * - * @return bool true si c'est une classe valide. il ne reste plus qu'à - * l'instancier avec {@link cons()} - */ - static final function check_class(&$class, &$args=null): bool { - if (self::is_class($class)) { - self::fix_class_args($class, $args); - return true; - } - return false; - } - - /** - * Comme {@link check_class()} mais lance une exception si la classe est - * invalide - * - * @throws ValueException si $class n'est pas une classe valide - */ - static final function ensure_class(&$class, &$args=null): void { - if (!self::check_class($class, $args)) { - throw ValueException::invalid_type($class, "class"); - } - } - - /** - * Instancier la classe avec les arguments spécifiés. - * Adapter $args en fonction du nombre réel d'arguments du constructeur - */ - static final function cons(string $class, ...$args) { - $c = new ReflectionClass($class); - $rf = $c->getConstructor(); - if ($rf === null) { - return $c->newInstance(); - } else { - if (!$rf->isVariadic()) { - $minArgs = $rf->getNumberOfRequiredParameters(); - $maxArgs = $rf->getNumberOfParameters(); - $args = array_slice($args, 0, $maxArgs); - while (count($args) < $minArgs) { - $args[] = null; - } - } - return $c->newInstanceArgs($args); - } - } -} diff --git a/php/tests/appTest.php b/php/tests/appTest.php deleted file mode 100644 index 8d86b6f..0000000 --- a/php/tests/appTest.php +++ /dev/null @@ -1,132 +0,0 @@ - $projdir, - "vendor" => [ - "bindir" => "$projdir/vendor/bin", - "autoload" => "$projdir/vendor/autoload.php", - ], - "appcode" => "nur-sery", - "cwd" => $cwd, - "datadir" => "$projdir/devel", - "etcdir" => "$projdir/devel/etc", - "vardir" => "$projdir/devel/var", - "logdir" => "$projdir/devel/log", - "profile" => "devel", - "appgroup" => null, - "name" => "my-application1", - "title" => null, - ], $app1->getParams()); - - $app2 = myapp::with(MyApplication2::class, $app1); - self::assertSame([ - "projdir" => $projdir, - "vendor" => [ - "bindir" => "$projdir/vendor/bin", - "autoload" => "$projdir/vendor/autoload.php", - ], - "appcode" => "nur-sery", - "cwd" => $cwd, - "datadir" => "$projdir/devel", - "etcdir" => "$projdir/devel/etc", - "vardir" => "$projdir/devel/var", - "logdir" => "$projdir/devel/log", - "profile" => "devel", - "appgroup" => null, - "name" => "my-application2", - "title" => null, - ], $app2->getParams()); - } - - function testInit() { - $projdir = config::get_projdir(); - $cwd = getcwd(); - - myapp::reset(); - myapp::init(MyApplication1::class); - self::assertSame([ - "projdir" => $projdir, - "vendor" => [ - "bindir" => "$projdir/vendor/bin", - "autoload" => "$projdir/vendor/autoload.php", - ], - "appcode" => "nur-sery", - "cwd" => $cwd, - "datadir" => "$projdir/devel", - "etcdir" => "$projdir/devel/etc", - "vardir" => "$projdir/devel/var", - "logdir" => "$projdir/devel/log", - "profile" => "devel", - "appgroup" => null, - "name" => "my-application1", - "title" => null, - ], myapp::get()->getParams()); - - myapp::init(MyApplication2::class); - self::assertSame([ - "projdir" => $projdir, - "vendor" => [ - "bindir" => "$projdir/vendor/bin", - "autoload" => "$projdir/vendor/autoload.php", - ], - "appcode" => "nur-sery", - "cwd" => $cwd, - "datadir" => "$projdir/devel", - "etcdir" => "$projdir/devel/etc", - "vardir" => "$projdir/devel/var", - "logdir" => "$projdir/devel/log", - "profile" => "devel", - "appgroup" => null, - "name" => "my-application2", - "title" => null, - ], myapp::get()->getParams()); - } - } -} - -namespace nulib\impl { - - use nulib\app\cli\Application; - use nulib\os\path; - use nulib\app; - - class config { - const PROJDIR = __DIR__.'/..'; - - static function get_projdir(): string { - return path::abspath(self::PROJDIR); - } - } - - class myapp extends app { - static function reset(): void { - self::$app = null; - } - } - - class MyApplication1 extends Application { - const PROJDIR = config::PROJDIR; - - function main() { - } - } - class MyApplication2 extends Application { - const PROJDIR = null; - - function main() { - } - } -} diff --git a/php/tests/php/funcTest.php b/php/tests/php/funcTest.php index 3f580fa..0f5200b 100644 --- a/php/tests/php/funcTest.php +++ b/php/tests/php/funcTest.php @@ -1108,6 +1108,27 @@ namespace nulib\php { self::assertException(ValueException::class, function() use ($func) { $func->bind(new C0())->invoke(); }); + self::assertSame(11, $func->bind(new C0(), false, true)->invoke()); + } + + function testModifyArgs() { + $closure = function(...$args) { return $args; }; + + self::assertSame(["x", "y", "z"], func::with($closure, ["a", "b", "c"])->replaceArgs(["x", "y", "z"])->invoke()); + + self::assertSame(["x", "y", "z", "a", "b", "c"], func::with($closure, ["a", "b", "c"])->prependArgs(null, ["x", "y", "z"])->invoke()); + self::assertSame(["x", "y", "z", "a", "b", "c"], func::with($closure, ["a", "b", "c"])->prependArgs(0, ["x", "y", "z"])->invoke()); + self::assertSame(["x", "y", "z", "b", "c"], func::with($closure, ["a", "b", "c"])->prependArgs(1, ["x", "y", "z"])->invoke()); + self::assertSame(["x", "y", "z", "c"], func::with($closure, ["a", "b", "c"])->prependArgs(2, ["x", "y", "z"])->invoke()); + self::assertSame(["x", "y", "z"], func::with($closure, ["a", "b", "c"])->prependArgs(3, ["x", "y", "z"])->invoke()); + self::assertSame(["x", "y", "z"], func::with($closure, ["a", "b", "c"])->prependArgs(4, ["x", "y", "z"])->invoke()); + + self::assertSame(["a", "b", "c", "x", "y", "z"], func::with($closure, ["a", "b", "c"])->appendArgs(null, ["x", "y", "z"])->invoke()); + self::assertSame(["a", "b", "c", "x", "y", "z"], func::with($closure, ["a", "b", "c"])->appendArgs(0, ["x", "y", "z"])->invoke()); + self::assertSame(["a", "b", "x", "y", "z"], func::with($closure, ["a", "b", "c"])->appendArgs(1, ["x", "y", "z"])->invoke()); + self::assertSame(["a", "x", "y", "z"], func::with($closure, ["a", "b", "c"])->appendArgs(2, ["x", "y", "z"])->invoke()); + self::assertSame(["x", "y", "z"], func::with($closure, ["a", "b", "c"])->appendArgs(3, ["x", "y", "z"])->invoke()); + self::assertSame(["x", "y", "z"], func::with($closure, ["a", "b", "c"])->appendArgs(4, ["x", "y", "z"])->invoke()); } } } diff --git a/php/tests/php/nur_funcTest.php b/php/tests/php/nur_funcTest.php deleted file mode 100644 index 44fa744..0000000 --- a/php/tests/php/nur_funcTest.php +++ /dev/null @@ -1,292 +0,0 @@ -")); - self::assertFalse(nur_func::is_method([])); - self::assertFalse(nur_func::is_method([""])); - self::assertFalse(nur_func::is_method([null, "->"])); - self::assertFalse(nur_func::is_method(["xxx", "->"])); - - self::assertTrue(nur_func::is_method("->xxx")); - self::assertTrue(nur_func::is_method(["->xxx"])); - self::assertTrue(nur_func::is_method([null, "->yyy"])); - self::assertTrue(nur_func::is_method(["xxx", "->yyy"])); - self::assertTrue(nur_func::is_method([null, "->yyy", "aaa"])); - self::assertTrue(nur_func::is_method(["xxx", "->yyy", "aaa"])); - } - - function testFix_method() { - $object = new \stdClass(); - $func= "->xxx"; - nur_func::fix_method($func, $object); - self::assertSame([$object, "xxx"], $func); - $func= ["->xxx"]; - nur_func::fix_method($func, $object); - self::assertSame([$object, "xxx"], $func); - $func= [null, "->yyy"]; - nur_func::fix_method($func, $object); - self::assertSame([$object, "yyy"], $func); - $func= ["xxx", "->yyy"]; - nur_func::fix_method($func, $object); - self::assertSame([$object, "yyy"], $func); - $func= [null, "->yyy", "aaa"]; - nur_func::fix_method($func, $object); - self::assertSame([$object, "yyy", "aaa"], $func); - $func= ["xxx", "->yyy", "aaa"]; - nur_func::fix_method($func, $object); - self::assertSame([$object, "yyy", "aaa"], $func); - } - - function testCall() { - self::assertSame(36, nur_func::call("func36")); - self::assertSame(12, nur_func::call(TC::class."::method")); - self::assertSame(12, nur_func::call([TC::class, "method"])); - $closure = function() { - return 21; - }; - self::assertSame(21, nur_func::call($closure)); - } - - function test_prepare_fill() { - # vérifier que les arguments sont bien remplis, en fonction du fait qu'ils - # soient obligatoires, facultatifs ou variadiques - - # m1 - self::assertSame([null], nur_func::call("func_m1")); - self::assertSame([null], nur_func::call("func_m1", null)); - self::assertSame([null], nur_func::call("func_m1", null, null)); - self::assertSame([null], nur_func::call("func_m1", null, null, null)); - self::assertSame([null], nur_func::call("func_m1", null, null, null, null)); - self::assertSame([1], nur_func::call("func_m1", 1)); - self::assertSame([1], nur_func::call("func_m1", 1, 2)); - self::assertSame([1], nur_func::call("func_m1", 1, 2, 3)); - self::assertSame([1], nur_func::call("func_m1", 1, 2, 3, 4)); - - # o1 - self::assertSame([9], nur_func::call("func_o1")); - self::assertSame([null], nur_func::call("func_o1", null)); - self::assertSame([null], nur_func::call("func_o1", null, null)); - self::assertSame([null], nur_func::call("func_o1", null, null, null)); - self::assertSame([null], nur_func::call("func_o1", null, null, null, null)); - self::assertSame([1], nur_func::call("func_o1", 1)); - self::assertSame([1], nur_func::call("func_o1", 1, 2)); - self::assertSame([1], nur_func::call("func_o1", 1, 2, 3)); - self::assertSame([1], nur_func::call("func_o1", 1, 2, 3, 4)); - - # v - self::assertSame([], nur_func::call("func_v")); - self::assertSame([null], nur_func::call("func_v", null)); - self::assertSame([null, null], nur_func::call("func_v", null, null)); - self::assertSame([null, null, null], nur_func::call("func_v", null, null, null)); - self::assertSame([null, null, null, null], nur_func::call("func_v", null, null, null, null)); - self::assertSame([1], nur_func::call("func_v", 1)); - self::assertSame([1, 2], nur_func::call("func_v", 1, 2)); - self::assertSame([1, 2, 3], nur_func::call("func_v", 1, 2, 3)); - self::assertSame([1, 2, 3, 4], nur_func::call("func_v", 1, 2, 3, 4)); - - # m1o1 - self::assertSame([null, 9], nur_func::call("func_m1o1")); - self::assertSame([null, 9], nur_func::call("func_m1o1", null)); - self::assertSame([null, null], nur_func::call("func_m1o1", null, null)); - self::assertSame([null, null], nur_func::call("func_m1o1", null, null, null)); - self::assertSame([null, null], nur_func::call("func_m1o1", null, null, null, null)); - self::assertSame([1, 9], nur_func::call("func_m1o1", 1)); - self::assertSame([1, 2], nur_func::call("func_m1o1", 1, 2)); - self::assertSame([1, 2], nur_func::call("func_m1o1", 1, 2, 3)); - self::assertSame([1, 2], nur_func::call("func_m1o1", 1, 2, 3, 4)); - - # m1v - self::assertSame([null], nur_func::call("func_m1v")); - self::assertSame([null], nur_func::call("func_m1v", null)); - self::assertSame([null, null], nur_func::call("func_m1v", null, null)); - self::assertSame([null, null, null], nur_func::call("func_m1v", null, null, null)); - self::assertSame([null, null, null, null], nur_func::call("func_m1v", null, null, null, null)); - self::assertSame([1], nur_func::call("func_m1v", 1)); - self::assertSame([1, 2], nur_func::call("func_m1v", 1, 2)); - self::assertSame([1, 2, 3], nur_func::call("func_m1v", 1, 2, 3)); - self::assertSame([1, 2, 3, 4], nur_func::call("func_m1v", 1, 2, 3, 4)); - - # m1o1v - self::assertSame([null, 9], nur_func::call("func_m1o1v")); - self::assertSame([null, 9], nur_func::call("func_m1o1v", null)); - self::assertSame([null, null], nur_func::call("func_m1o1v", null, null)); - self::assertSame([null, null, null], nur_func::call("func_m1o1v", null, null, null)); - self::assertSame([null, null, null, null], nur_func::call("func_m1o1v", null, null, null, null)); - self::assertSame([1, 9], nur_func::call("func_m1o1v", 1)); - self::assertSame([1, 2], nur_func::call("func_m1o1v", 1, 2)); - self::assertSame([1, 2, 3], nur_func::call("func_m1o1v", 1, 2, 3)); - self::assertSame([1, 2, 3, 4], nur_func::call("func_m1o1v", 1, 2, 3, 4)); - - # o1v - self::assertSame([9], nur_func::call("func_o1v")); - self::assertSame([null], nur_func::call("func_o1v", null)); - self::assertSame([null, null], nur_func::call("func_o1v", null, null)); - self::assertSame([null, null, null], nur_func::call("func_o1v", null, null, null)); - self::assertSame([null, null, null, null], nur_func::call("func_o1v", null, null, null, null)); - self::assertSame([1], nur_func::call("func_o1v", 1)); - self::assertSame([1, 2], nur_func::call("func_o1v", 1, 2)); - self::assertSame([1, 2, 3], nur_func::call("func_o1v", 1, 2, 3)); - self::assertSame([1, 2, 3, 4], nur_func::call("func_o1v", 1, 2, 3, 4)); - } - - function testCall_all() { - $c1 = new C1(); - $c2 = new C2(); - $c3 = new C3(); - - self::assertSameValues([11, 12], nur_func::call_all(C1::class)); - self::assertSameValues([11, 12, 21, 22], nur_func::call_all($c1)); - self::assertSameValues([13, 11, 12], nur_func::call_all(C2::class)); - self::assertSameValues([13, 23, 11, 12, 21, 22], nur_func::call_all($c2)); - self::assertSameValues([111, 13, 12], nur_func::call_all(C3::class)); - self::assertSameValues([111, 121, 13, 23, 12, 22], nur_func::call_all($c3)); - - $options = "conf"; - self::assertSameValues([11], nur_func::call_all(C1::class, $options)); - self::assertSameValues([11, 21], nur_func::call_all($c1, $options)); - self::assertSameValues([11], nur_func::call_all(C2::class, $options)); - self::assertSameValues([11, 21], nur_func::call_all($c2, $options)); - self::assertSameValues([111], nur_func::call_all(C3::class, $options)); - self::assertSameValues([111, 121], nur_func::call_all($c3, $options)); - - $options = ["prefix" => "conf"]; - self::assertSameValues([11], nur_func::call_all(C1::class, $options)); - self::assertSameValues([11, 21], nur_func::call_all($c1, $options)); - self::assertSameValues([11], nur_func::call_all(C2::class, $options)); - self::assertSameValues([11, 21], nur_func::call_all($c2, $options)); - self::assertSameValues([111], nur_func::call_all(C3::class, $options)); - self::assertSameValues([111, 121], nur_func::call_all($c3, $options)); - - self::assertSameValues([11, 12], nur_func::call_all($c1, ["include" => "x"])); - self::assertSameValues([11, 21], nur_func::call_all($c1, ["include" => "y"])); - self::assertSameValues([11, 12, 21], nur_func::call_all($c1, ["include" => ["x", "y"]])); - - self::assertSameValues([21, 22], nur_func::call_all($c1, ["exclude" => "x"])); - self::assertSameValues([12, 22], nur_func::call_all($c1, ["exclude" => "y"])); - self::assertSameValues([22], nur_func::call_all($c1, ["exclude" => ["x", "y"]])); - - self::assertSameValues([12], nur_func::call_all($c1, ["include" => "x", "exclude" => "y"])); - } - - function testCons() { - $obj1 = nur_func::cons(WoCons::class, 1, 2, 3); - self::assertInstanceOf(WoCons::class, $obj1); - - $obj2 = nur_func::cons(WithEmptyCons::class, 1, 2, 3); - self::assertInstanceOf(WithEmptyCons::class, $obj2); - - $obj3 = nur_func::cons(WithCons::class, 1, 2, 3); - self::assertInstanceOf(WithCons::class, $obj3); - self::assertSame(1, $obj3->first); - } - } - - class WoCons { - } - class WithEmptyCons { - function __construct() { - } - } - class WithCons { - public $first; - function __construct($first) { - $this->first = $first; - } - } - - class TC { - static function method() { - return 12; - } - } - - class C1 { - static function confps1_xy() { - return 11; - } - static function ps2_x() { - return 12; - } - function confp1_y() { - return 21; - } - function p2() { - return 22; - } - } - class C2 extends C1 { - static function ps3() { - return 13; - } - function p3() { - return 23; - } - } - class C3 extends C2 { - static function confps1_xy() { - return 111; - } - function confp1_y() { - return 121; - } - } -}