From a5b0d7ac0af6274f5fc91dde2a964e5828bcbc93 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Thu, 19 Sep 2024 01:01:22 +0400 Subject: [PATCH] modifs.mineures sans commentaires --- src/cl.php | 6 +- src/db/Capacitor.php | 4 +- src/db/CapacitorStorage.php | 28 +- src/db/pdo/Pdo.php | 8 +- src/db/pdo/_config.php | 8 +- src/db/sqlite/Sqlite.php | 4 +- src/db/sqlite/_config.php | 8 +- src/db/sqlite/_migration.php | 8 +- src/ext/spreadsheet/SpoutBuilder.php | 6 +- src/file/csv/AbstractBuilder.php | 8 +- src/output/msg.php | 14 +- src/php/content/Printer.php | 4 +- src/php/content/c.php | 22 +- src/php/func.php | 994 +++++++----- src/php/mprop.php | 4 +- wip/php/orig_func.php => src/php/nur_func.php | 206 +-- tests/php/funcTest.php | 1363 +++++++++++++---- tests/php/nur_funcTest.php | 292 ++++ tests/wip/php/funcTest.php | 1146 -------------- tests/wip/php/impl/C0.php | 15 - tests/wip/php/impl/C1.php | 18 - tests/wip/php/impl/SC.php | 12 - wip/cli/ArgsParser.php | 20 +- wip/cli/DynamicCommand.php | 4 +- wip/cli/DynamicCommandMethod.php | 6 +- wip/php/func.php | 630 -------- wip/schema/types/tcallable.php | 4 +- 27 files changed, 2131 insertions(+), 2711 deletions(-) rename wip/php/orig_func.php => src/php/nur_func.php (70%) create mode 100644 tests/php/nur_funcTest.php delete mode 100644 tests/wip/php/funcTest.php delete mode 100644 tests/wip/php/impl/C0.php delete mode 100644 tests/wip/php/impl/C1.php delete mode 100644 tests/wip/php/impl/SC.php delete mode 100644 wip/php/func.php diff --git a/src/cl.php b/src/cl.php index 0334e09..9e406a4 100644 --- a/src/cl.php +++ b/src/cl.php @@ -2,7 +2,7 @@ namespace nur\sery; use ArrayAccess; -use nur\sery\php\func; +use nur\sery\php\nur_func; use Traversable; /** @@ -348,9 +348,9 @@ class cl { static final function map(callable $callback, ?iterable $array): array { $result = []; if ($array !== null) { - $ctx = func::_prepare($callback); + $ctx = nur_func::_prepare($callback); foreach ($array as $key => $value) { - $result[$key] = func::_call($ctx, [$value, $key]); + $result[$key] = nur_func::_call($ctx, [$value, $key]); } } return $result; diff --git a/src/db/Capacitor.php b/src/db/Capacitor.php index 274f1ae..e3b2b07 100644 --- a/src/db/Capacitor.php +++ b/src/db/Capacitor.php @@ -1,7 +1,7 @@ commit(); $commited = true; diff --git a/src/db/CapacitorStorage.php b/src/db/CapacitorStorage.php index 7e1dca4..56ebb3d 100644 --- a/src/db/CapacitorStorage.php +++ b/src/db/CapacitorStorage.php @@ -3,7 +3,7 @@ namespace nur\sery\db; use nur\sery\cl; use nur\sery\db\cache\cache; -use nur\sery\php\func; +use nur\sery\php\nur_func; use nur\sery\ValueException; use Traversable; @@ -232,8 +232,8 @@ EOT; $initFunc = [$channel, "getItemValues"]; $initArgs = $args; - func::ensure_func($initFunc, null, $initArgs); - $values = func::call($initFunc, $item, ...$initArgs); + nur_func::ensure_func($initFunc, null, $initArgs); + $values = nur_func::call($initFunc, $item, ...$initArgs); if ($values === [false]) return 0; $row = cl::merge( @@ -261,7 +261,7 @@ EOT; $insert = true; $initFunc = [$channel, "onCreate"]; $initArgs = $args; - func::ensure_func($initFunc, null, $initArgs); + nur_func::ensure_func($initFunc, null, $initArgs); $values = $this->unserialize($channel, $row); $pvalues = null; } else { @@ -278,12 +278,12 @@ EOT; } $initFunc = [$channel, "onUpdate"]; $initArgs = $args; - func::ensure_func($initFunc, null, $initArgs); + nur_func::ensure_func($initFunc, null, $initArgs); $values = $this->unserialize($channel, $row); $pvalues = $this->unserialize($channel, $prow); } - $updates = func::call($initFunc, $item, $values, $pvalues, ...$initArgs); + $updates = nur_func::call($initFunc, $item, $values, $pvalues, ...$initArgs); if ($updates === [false]) return 0; if (is_array($updates) && $updates) { if ($insert === null) $insert = false; @@ -295,8 +295,8 @@ EOT; } if ($func !== null) { - func::ensure_func($func, $channel, $args); - $updates = func::call($func, $item, $values, $pvalues, ...$args); + nur_func::ensure_func($func, $channel, $args); + $updates = nur_func::call($func, $item, $values, $pvalues, ...$args); if ($updates === [false]) return 0; if (is_array($updates) && $updates) { if ($insert === null) $insert = false; @@ -510,8 +510,8 @@ 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; - func::ensure_func($func, $channel, $args); - $onEach = func::_prepare($func); + nur_func::ensure_func($func, $channel, $args); + $onEach = nur_func::_prepare($func); $db = $this->db(); # si on est déjà dans une transaction, désactiver la gestion des transactions $manageTransactions = $channel->isManageTransactions() && !$db->inTransaction(); @@ -528,7 +528,7 @@ EOT; $all = $this->_allCached("each", $channel, $filter, $mergeQuery); foreach ($all as $values) { $rowIds = $this->getRowIds($channel, $values); - $updates = func::_call($onEach, [$values["item"], $values, ...$args]); + $updates = nur_func::_call($onEach, [$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 +579,8 @@ EOT; function _delete(CapacitorChannel $channel, $filter, $func, ?array $args): int { $this->_create($channel); if ($func === null) $func = CapacitorChannel::onDelete; - func::ensure_func($func, $channel, $args); - $onEach = func::_prepare($func); + nur_func::ensure_func($func, $channel, $args); + $onEach = nur_func::_prepare($func); $db = $this->db(); # si on est déjà dans une transaction, désactiver la gestion des transactions $manageTransactions = $channel->isManageTransactions() && !$db->inTransaction(); @@ -596,7 +596,7 @@ EOT; $all = $this->_allCached("delete", $channel, $filter); foreach ($all as $values) { $rowIds = $this->getRowIds($channel, $values); - $delete = boolval(func::_call($onEach, [$values["item"], $values, ...$args])); + $delete = boolval(nur_func::_call($onEach, [$values["item"], $values, ...$args])); if ($delete) { $db->exec([ "delete", diff --git a/src/db/pdo/Pdo.php b/src/db/pdo/Pdo.php index d51c073..cd4c22a 100644 --- a/src/db/pdo/Pdo.php +++ b/src/db/pdo/Pdo.php @@ -5,7 +5,7 @@ use Generator; use nur\sery\cl; use nur\sery\db\IDatabase; use nur\sery\db\ITransactor; -use nur\sery\php\func; +use nur\sery\php\nur_func; use nur\sery\php\time\Date; use nur\sery\php\time\DateTime; use nur\sery\ValueException; @@ -118,8 +118,8 @@ class Pdo implements IDatabase { $dbconn = $this->dbconn; $options = $this->options; if (is_callable($options)) { - func::ensure_func($options, $this, $args); - $options = func::call($options, ...$args); + nur_func::ensure_func($options, $this, $args); + $options = nur_func::call($options, ...$args); } $this->db = new \PDO($dbconn["name"], $dbconn["user"], $dbconn["pass"], $options); _config::with($this->config)->configure($this); @@ -190,7 +190,7 @@ class Pdo implements IDatabase { if ($func !== null) { $commited = false; try { - func::call($func, $this); + nur_func::call($func, $this); if ($commit) { $this->commit(); $commited = true; diff --git a/src/db/pdo/_config.php b/src/db/pdo/_config.php index 7566b70..db6ed24 100644 --- a/src/db/pdo/_config.php +++ b/src/db/pdo/_config.php @@ -1,7 +1,7 @@ configs as $key => $config) { - if (is_string($config) && !func::is_method($config)) { + if (is_string($config) && !nur_func::is_method($config)) { $pdo->exec($config); } else { - func::ensure_func($config, $this, $args); - func::call($config, $pdo, $key, ...$args); + nur_func::ensure_func($config, $this, $args); + nur_func::call($config, $pdo, $key, ...$args); } } } diff --git a/src/db/sqlite/Sqlite.php b/src/db/sqlite/Sqlite.php index 4692014..874458c 100644 --- a/src/db/sqlite/Sqlite.php +++ b/src/db/sqlite/Sqlite.php @@ -5,7 +5,7 @@ use Generator; use nur\sery\cl; use nur\sery\db\IDatabase; use nur\sery\db\ITransactor; -use nur\sery\php\func; +use nur\sery\php\nur_func; use nur\sery\ValueException; use SQLite3; use SQLite3Result; @@ -234,7 +234,7 @@ class Sqlite implements IDatabase { if ($func !== null) { $commited = false; try { - func::call($func, $this); + nur_func::call($func, $this); if ($commit) { $this->commit(); $commited = true; diff --git a/src/db/sqlite/_config.php b/src/db/sqlite/_config.php index ee493a9..270e57e 100644 --- a/src/db/sqlite/_config.php +++ b/src/db/sqlite/_config.php @@ -1,7 +1,7 @@ configs as $key => $config) { - if (is_string($config) && !func::is_method($config)) { + if (is_string($config) && !nur_func::is_method($config)) { $sqlite->exec($config); } else { - func::ensure_func($config, $this, $args); - func::call($config, $sqlite, $key, ...$args); + nur_func::ensure_func($config, $this, $args); + nur_func::call($config, $sqlite, $key, ...$args); } } } diff --git a/src/db/sqlite/_migration.php b/src/db/sqlite/_migration.php index b532d14..2736cb6 100644 --- a/src/db/sqlite/_migration.php +++ b/src/db/sqlite/_migration.php @@ -1,7 +1,7 @@ $migration, "done" => 0, ]); - if (is_string($migration) && !func::is_method($migration)) { + if (is_string($migration) && !nur_func::is_method($migration)) { $sqlite->exec($migration); } else { - func::ensure_func($migration, $this, $args); - func::call($migration, $sqlite, $key, ...$args); + nur_func::ensure_func($migration, $this, $args); + nur_func::call($migration, $sqlite, $key, ...$args); } $sqlite->exec("update _migration set done = 1 where key = :key", [ "key" => $key, diff --git a/src/ext/spreadsheet/SpoutBuilder.php b/src/ext/spreadsheet/SpoutBuilder.php index 113d7dc..4ae195a 100644 --- a/src/ext/spreadsheet/SpoutBuilder.php +++ b/src/ext/spreadsheet/SpoutBuilder.php @@ -4,7 +4,7 @@ namespace nur\sery\ext\spreadsheet; use nur\sery\file\csv\AbstractBuilder; use nur\sery\file\csv\TAbstractBuilder; use nur\sery\os\path; -use nur\sery\php\func; +use nur\sery\php\nur_func; use nur\sery\php\time\Date; use nur\sery\php\time\DateTime; use nur\sery\web\http; @@ -110,8 +110,8 @@ class SpoutBuilder extends AbstractBuilder { $cookFunc = $params["cook_func"] ?? null; $cookCtx = $cookArgs = null; if ($cookFunc !== null) { - func::ensure_func($cookFunc, $this, $cookArgs); - $cookCtx = func::_prepare($cookFunc); + nur_func::ensure_func($cookFunc, $this, $cookArgs); + $cookCtx = nur_func::_prepare($cookFunc); } $this->cookCtx = $cookCtx; $this->cookArgs = $cookArgs; diff --git a/src/file/csv/AbstractBuilder.php b/src/file/csv/AbstractBuilder.php index a21b87a..1ea4045 100644 --- a/src/file/csv/AbstractBuilder.php +++ b/src/file/csv/AbstractBuilder.php @@ -5,7 +5,7 @@ use DateTimeInterface; use nur\sery\cl; use nur\sery\file\TempStream; use nur\sery\os\path; -use nur\sery\php\func; +use nur\sery\php\nur_func; use nur\sery\php\time\DateTime; use nur\sery\web\http; @@ -29,8 +29,8 @@ abstract class AbstractBuilder extends TempStream implements IBuilder { $cookFunc = $params["cook_func"] ?? null; $cookCtx = $cookArgs = null; if ($cookFunc !== null) { - func::ensure_func($cookFunc, $this, $cookArgs); - $cookCtx = func::_prepare($cookFunc); + nur_func::ensure_func($cookFunc, $this, $cookArgs); + $cookCtx = nur_func::_prepare($cookFunc); } $this->cookCtx = $cookCtx; $this->cookArgs = $cookArgs; @@ -75,7 +75,7 @@ abstract class AbstractBuilder extends TempStream implements IBuilder { protected function cookRow(?array $row): ?array { if ($this->cookCtx !== null) { $args = cl::merge([$row], $this->cookArgs); - $row = func::_call($this->cookCtx, $args); + $row = nur_func::_call($this->cookCtx, $args); } if ($row !== null) { foreach ($row as &$value) { diff --git a/src/output/msg.php b/src/output/msg.php index ba930cb..37b8583 100644 --- a/src/output/msg.php +++ b/src/output/msg.php @@ -2,7 +2,7 @@ namespace nur\sery\output; use nur\sery\output\std\ProxyMessenger; -use nur\sery\php\func; +use nur\sery\php\nur_func; /** * Class msg: inscrire un message dans les logs ET l'afficher sur la console @@ -40,8 +40,8 @@ class msg extends _messenger { if ($log instanceof IMessenger) log::set_messenger($log); elseif (is_string($log)) log::set_messenger_class($log); elseif (is_array($log)) { - func::ensure_class($log, $args); - $log = func::cons($log, $args); + nur_func::ensure_class($log, $args); + $log = nur_func::cons($log, $args); } log::set_messenger($log); $msgs[] = $log; @@ -50,8 +50,8 @@ class msg extends _messenger { if ($console instanceof IMessenger) console::set_messenger($console); elseif (is_string($console)) console::set_messenger_class($console); elseif (is_array($console)) { - func::ensure_class($console, $args); - $console = func::cons($console, $args); + nur_func::ensure_class($console, $args); + $console = nur_func::cons($console, $args); } console::set_messenger($console); $msgs[] = $console; @@ -60,8 +60,8 @@ class msg extends _messenger { if ($say instanceof IMessenger) say::set_messenger($say); elseif (is_string($say)) say::set_messenger_class($say); elseif (is_array($say)) { - func::ensure_class($say, $args); - $say = func::cons($say, $args); + nur_func::ensure_class($say, $args); + $say = nur_func::cons($say, $args); } say::set_messenger($say); $msgs[] = $say; diff --git a/src/php/content/Printer.php b/src/php/content/Printer.php index b030b0b..77f67c8 100644 --- a/src/php/content/Printer.php +++ b/src/php/content/Printer.php @@ -1,7 +1,7 @@ content, $name], ...$args); + $content = nur_func::call([$this->content, $name], ...$args); c::write($content); } } diff --git a/src/php/content/c.php b/src/php/content/c.php index ec18aeb..771fb2f 100644 --- a/src/php/content/c.php +++ b/src/php/content/c.php @@ -3,7 +3,7 @@ namespace nur\sery\php\content; use Closure; use nur\sery\cl; -use nur\sery\php\func; +use nur\sery\php\nur_func; /** * Class c: classe outil pour gérer du contenu @@ -62,8 +62,8 @@ 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; - func::ensure_func($func, $object_or_class, $args); - $values = self::q(func::call($func, ...$args)); + nur_func::ensure_func($func, $object_or_class, $args); + $values = self::q(nur_func::call($func, ...$args)); self::add_static_content($dest, $values, $key, $seq); continue; } @@ -83,15 +83,15 @@ class c { $arg = self::resolve($arg, $object_or_class, false); if (!$array) $arg = $arg[0]; }; unset($arg); - if (func::is_static($func)) { - func::ensure_func($func, $object_or_class, $args); - $value = func::call($func, ...$args); - } elseif (func::is_class($func)) { - func::fix_class_args($func, $args); - $value = func::cons($func, ...$args); + 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 { - func::ensure_func($func, $object_or_class, $args); - $value = func::call($func, ...$args); + nur_func::ensure_func($func, $object_or_class, $args); + $value = nur_func::call($func, ...$args); } } } diff --git a/src/php/func.php b/src/php/func.php index 7a9a305..126c909 100644 --- a/src/php/func.php +++ b/src/php/func.php @@ -2,452 +2,620 @@ namespace nur\sery\php; use Closure; -use nur\sery\cl; -use nur\sery\ref\php\ref_func; +use nur\sery\cv; +use nur\sery\StateException; use nur\sery\ValueException; use ReflectionClass; use ReflectionFunction; use ReflectionMethod; /** - * Class func: outils pour appeler des fonctions et méthodes dynamiquement + * Class func: outils pour appeler fonctions et méthodes dynamiquement + * + * les types de fonctions supportés sont: + * - fonctions (globales ou dans un namespace) + * - classes (l'appel de cette "fonction" provoque l'instanciation de la classe) + * - méthodes (statiques ou non, liables à une classe ou un objet) + * - Closure */ class func { - /** - * tester si $func est une chaine de la forme "XXX::method" où 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 $allowClass=false): bool { - if (is_string($func)) { - $pos = strpos($func, "::"); - if ($pos === false) return false; - return $pos + 2 < strlen($func); - } elseif (is_array($func) && array_key_exists(0, $func)) { - $count = count($func); - if ($count == 1) { - 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; - if (!is_string($func[1]) || strlen($func[1]) == 0) return false; - if (strpos($func[1], "\\") !== false) return false; - return true; - } - } - return false; + private static function _is_invalid(?string $f): bool { + return $f === null || $f === "" || $f === "::" || $f === "->"; } - /** - * 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; + private static function _is_nfunction(?string $f): bool { + return strpos($f, "\\") !== 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 final 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; - } - } + private static function _parse_static(?string &$m): bool { + $pos = strpos($m, "::"); + if ($pos === false) return false; + $m = substr($m, $pos + 2); return true; } - /** @var Schema */ - private static $call_all_params_schema; + private static function _parse_method(?string &$m): bool { + $pos = strpos($m, "->"); + if ($pos === false) return false; + $m = substr($m, $pos + 2); + return true; + } + + ############################################################################# + # Fonctions /** - * 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} + * vérifier que $func est une fonction et la normaliser le cas échéant. + * retourner true si c'est une fonction, false sinon + * + * les formes suivantes sont supportées: + * - "function" si une classe du même nom n'existe pas déjà + * - [false, "function", ...$args] c'est la forme normalisée + * + * @param bool $strict vérifier l'inexistence de la classe et l'existence de + * la fonction (ne pas uniquement faire une vérification syntaxique) */ - 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]; + static function verifix_function(&$func, bool $strict=true, ?string &$reason=null): bool { + if ($strict) { + $msg = var_export($func, true); + $reason = null; } - 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; + if ($func instanceof ReflectionFunction) return true; + if (is_string($func)) { + $c = false; + $f = $func; + } elseif (is_array($func)) { + if (!array_key_exists(0, $func)) return false; + $c = $func[0]; + if (!array_key_exists(1, $func)) return false; + $f = $func[1]; } else { - throw new ValueException("$class_or_object: vous devez spécifier une classe ou un objet"); + return false; } - $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]; + if ($c !== false) return false; + if (!is_string($f)) return false; + if (self::_is_invalid($f)) return false; + if (self::_parse_static($f)) return false; + if (self::_parse_method($f)) return false; + if ($strict) { + $reason = null; + if (class_exists($f)) { + $reason = "$msg: is a class"; + return false; + } + if (!function_exists($f)) { + $reason = "$msg: function not found"; + return false; } - return true; } - return false; + $func = [false, $f]; + return true; } /** - * s'assurer que $class est une classe et renseigner le cas échéant les - * arguments. + * vérifier que $func est une fonction avec les règles de + * {@link self::verifix_function()} + */ + static function is_function($func, bool $strict=true, ?string &$reason=null): bool { + return self::verifix_function($func, $strict, $reason); + } + + ############################################################################# + # Classes + + /** + * vérifier que $func est une classe et la normaliser le cas échéant. + * retourner true si c'est une classe, false sinon * - * @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 + * les formes suivantes sont supportées: + * - "class" + * - ["class", false, ...$args] c'est la forme normalisée * - * @throws ValueException si $class n'est pas une classe valide + * @param bool $strict vérifier l'existence de la classe (ne pas uniquement + * faire une vérification syntaxique) */ - static final function ensure_class(&$class, &$args=null): void { - if (!self::check_class($class, $args)) { - throw ValueException::invalid_type($class, "class"); + static function verifix_class(&$func, bool $strict=true, ?string &$reason=null): bool { + if ($strict) { + $msg = var_export($func, true); + $reason = null; } - } - - /** - * 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(); + if ($func instanceof ReflectionClass) return true; + if (is_string($func)) { + $c = $func; + $f = false; + } elseif (is_array($func)) { + if (!array_key_exists(0, $func)) return false; + $c = $func[0]; + if (!array_key_exists(1, $func)) return false; + $f = $func[1]; } else { - if (!$rf->isVariadic()) { - $minArgs = $rf->getNumberOfRequiredParameters(); - $maxArgs = $rf->getNumberOfParameters(); - $args = array_slice($args, 0, $maxArgs); - while (count($args) < $minArgs) { - $args[] = null; + return false; + } + if (!is_string($c)) return false; + if (self::_is_invalid($c)) return false; + if (self::_parse_static($c)) return false; + if (self::_parse_method($c)) return false; + if ($f !== false) return false; + if ($strict) { + if (!class_exists($c)) { + $reason = "$msg: class not found"; + return false; + } + } + $func = [$c, false]; + return true; + } + + /** + * vérifier que $func est une classe avec les règles de + * {@link self::verifix_class()} + */ + static function is_class($func, bool $strict=true, ?string &$reason=null): bool { + return self::verifix_class($func, $strict, $reason); + } + + ############################################################################# + # Méthodes statiques + + private static function _parse_class_s(?string $cs, ?string &$c, ?string &$s): bool { + if (self::_is_invalid($cs) || self::_parse_method($cs)) return false; + $pos = strpos($cs, "::"); + if ($pos === false) return false; + if ($pos === 0) return false; + $tmpc = substr($cs, 0, $pos); + $cs = substr($cs, $pos + 2); + if (self::_is_nfunction($cs)) return false; + [$c, $s] = [$tmpc, cv::vn($cs)]; + return true; + } + + private static function _parse_c_static(?string $cs, ?string &$c, ?string &$s, ?bool &$bound): bool { + if (self::_is_invalid($cs) || self::_parse_method($cs)) return false; + $pos = strpos($cs, "::"); + if ($pos === false) return false; + if ($pos == strlen($cs) - 2) return false; + if ($pos > 0) { + $tmpc = substr($cs, 0, $pos); + $bound = true; + } else { + $tmpc = null; + $bound = false; + } + $cs = substr($cs, $pos + 2); + if (self::_is_nfunction($cs)) return false; + [$c, $s] = [$tmpc, cv::vn($cs)]; + return true; + } + + /** + * vérifier que $func est une méthode statique, et la normaliser le cas + * échéant. retourner true si c'est une méthode statique, false sinon + * + * les formes suivantes sont supportées (XXX étant null ou n'importe quelle + * valeur scalaire de n'importe quel type sauf false) + * - "XXX::function" + * - ["XXX::function", ...$args] + * - [XXX, "::function", ...$args] + * - [XXX, "function", ...$args] c'est la forme normalisée + * + * Si XXX est une classe, la méthode statique est liée. sinon, elle doit être + * liée à une classe avant d'être utilisée + * + * @param bool $strict vérifier l'existence de la classe et de la méthode si + * la méthode est liée (ne pas uniquement faire une vérification syntaxique) + */ + static function verifix_static(&$func, bool $strict=true, ?bool &$bound=null, ?string &$reason=null): bool { + if ($strict) { + $msg = var_export($func, true); + $reason = null; + } + if ($func instanceof ReflectionMethod) { + $bound = false; + return true; + } + if (is_string($func)) { + if (!self::_parse_c_static($func, $c, $f, $bound)) return false; + $cf = [$c, $f]; + } elseif (is_array($func)) { + $cf = $func; + if (!array_key_exists(0, $cf)) return false; + $c = $cf[0]; + if ($c === false) return false; + if (is_object($c)) $c = get_class($c); + if (is_string($c)) { + if (self::_is_invalid($c)) return false; + if (self::_parse_class_s($c, $c, $f)) { + $cf[0] = $c; + if ($f !== null) { + # ["class::method"] --> ["class", "method"] + array_splice($cf, 1, 0, [$f]); + } + $bound = true; + } elseif (self::_parse_c_static($c, $c, $f, $bound)) { + # ["::method"] --> [null, "method"] + array_splice($cf, 0, 0, [null]); + $cf[1] = $f; + } else { + $cf[0] = $c; + $bound = is_string($c); + } + } else { + $cf[0] = null; + $bound = false; + } + # + if (!array_key_exists(1, $cf)) return false; + $f = $cf[1]; + if (!is_string($f)) return false; + if (self::_parse_c_static($f, $rc, $f, $rbound)) { + if ($rc !== null && $c === null) { + $c = $rc; + $bound = $rbound; + } + } else { + if (self::_is_invalid($f)) return false; + if (self::_is_nfunction($f)) return false; + if (self::_parse_method($f)) return false; + self::_parse_static($f); + } + $cf[1] = $f; + } else { + return false; + } + if ($strict) { + $reason = null; + if ($bound) { + if (!class_exists($c)) { + $reason = "$msg: class not found"; + return false; + } + if (!method_exists($c, $f)) { + $reason = "$msg: method not found"; + return false; + } + } else { + $reason = "$msg: not bound"; + } + } + $func = $cf; + return true; + } + + /** + * vérifier que $func est une méthode statique avec les règles de + * {@link self::verifix_static()} + */ + static function is_static($func, bool $strict=true, ?bool &$bound=null, ?string &$reason=null): bool { + return self::verifix_static($func, $strict, $bound, $reason); + } + + ############################################################################# + # Méthodes non statiques + + private static function _parse_class_m(?string $cm, ?string &$c, ?string &$m): bool { + if (self::_is_invalid($cm) || self::_parse_static($cm)) return false; + $pos = strpos($cm, "->"); + if ($pos === false) return false; + if ($pos === 0) return false; + $tmpc = substr($cm, 0, $pos); + $cm = substr($cm, $pos + 2); + if (self::_is_nfunction($cm)) return false; + [$c, $m] = [$tmpc, cv::vn($cm)]; + return true; + } + + private static function _parse_c_method(?string $cm, ?string &$c, ?string &$m, ?bool &$bound): bool { + if (self::_is_invalid($cm) || self::_parse_static($cm)) return false; + $pos = strpos($cm, "->"); + if ($pos === false) return false; + if ($pos == strlen($cm) - 2) return false; + if ($pos > 0) { + $tmpc = substr($cm, 0, $pos); + $bound = true; + } else { + $tmpc = null; + $bound = false; + } + $cm = substr($cm, $pos + 2); + if (self::_is_nfunction($cm)) return false; + [$c, $m] = [$tmpc, cv::vn($cm)]; + return true; + } + + /** + * vérifier que $func est une méthode non statique, et la normaliser le cas + * échéant. retourner true si c'est une méthode non statique, false sinon + * + * les formes suivantes sont supportées (XXX étant null ou n'importe quelle + * valeur scalaire de n'importe quel type sauf false) + * - "XXX->function" + * - ["XXX->function", ...$args] + * - [XXX, "->function", ...$args] + * - [XXX, "function", ...$args] c'est la forme normalisée + * + * Si XXX est une classe ou un objet, la méthode est liée. dans tous les cas, + * elle doit être liée à un objet avant d'être utilisée + * + * @param bool $strict vérifier l'existence de la classe et de la méthode si + * la méthode est liée (ne pas uniquement faire une vérification syntaxique) + */ + static function verifix_method(&$func, bool $strict=true, ?bool &$bound=null, ?string &$reason=null): bool { + if ($strict) { + $msg = var_export($func, true); + $reason = null; + } + if ($func instanceof ReflectionMethod) { + $bound = false; + return true; + } + if (is_string($func)) { + if (!self::_parse_c_method($func, $c, $f, $bound)) return false; + $cf = [$c, $f]; + } elseif (is_array($func)) { + $cf = $func; + if (!array_key_exists(0, $cf)) return false; + $c = $cf[0]; + if ($c === false) return false; + if (is_object($c)) { + $bound = true; + } elseif (is_string($c)) { + if (self::_is_invalid($c)) return false; + if (self::_parse_class_m($c, $c, $f)) { + $cf[0] = $c; + if ($f !== null) { + # ["class->method"] --> ["class", "method"] + array_splice($cf, 1, 0, [$f]); + } + $bound = true; + } elseif (self::_parse_c_method($c, $c, $f, $bound)) { + # ["->method"] --> [null, "method"] + array_splice($cf, 0, 0, [null]); + $cf[1] = $f; + } else { + $cf[0] = $c; + $bound = is_string($c); + } + } else { + $cf[0] = null; + $bound = false; + } + # + if (!array_key_exists(1, $cf)) return false; + $f = $cf[1]; + if (!is_string($f)) return false; + if (self::_parse_c_method($f, $rc, $f, $rbound)) { + if ($rc !== null && $c === null) { + $c = $rc; + $bound = $rbound; + } + } else { + if (self::_is_invalid($f)) return false; + if (self::_is_nfunction($f)) return false; + if (self::_parse_static($f)) return false; + self::_parse_method($f); + } + $cf[1] = $f; + } else { + return false; + } + if ($strict) { + $reason = null; + if ($bound) { + if (!is_object($c) && !class_exists($c)) { + $reason = "$msg: class not found"; + return false; + } + if (!method_exists($c, $f)) { + $reason = "$msg: method not found"; + return false; + } + } else { + $reason = "$msg: not bound"; + } + } + $func = $cf; + return true; + } + + /** + * vérifier que $func est une méthode non statique avec les règles de + * {@link self::verifix_method()} + */ + static function is_method($func, bool $strict=true, ?bool &$bound=null, ?string &$reason=null): bool { + return self::verifix_method($func, $strict, $bound, $reason); + } + + ############################################################################# + # func + + const TYPE_CLOSURE = 0, TYPE_FUNCTION = 1, TYPE_CLASS = 2, TYPE_METHOD = 3; + + protected static function not_a_callable($func, ?string $reason) { + if ($reason === null) { + $msg = var_export($func, true); + $reason = "$msg: not a callable"; + } + return new ValueException($reason); + } + + static function with($func, bool $strict=true): self { + if (!is_array($func)) { + if ($func instanceof Closure) { + return new self(self::TYPE_CLOSURE, $func); + } elseif ($func instanceof ReflectionFunction) { + return new self(self::TYPE_FUNCTION, $func); + } elseif ($func instanceof ReflectionClass) { + return new self(self::TYPE_CLASS, $func); + } elseif ($func instanceof ReflectionMethod) { + return new self(self::TYPE_METHOD, $func, false); + } + } + if (self::verifix_function($func, $strict, $reason)) { + return new self(self::TYPE_FUNCTION, $func, false, $reason); + } elseif (self::verifix_class($func, $strict, $reason)) { + return new self(self::TYPE_CLASS, $func, false, $reason); + } else { + $valid = true; + if (is_array($func) && array_key_exists(0, $func) && is_object($func[0])) { + if (self::verifix_method($func, $strict, $bound, $reason)) { + $type = self::TYPE_METHOD; + } elseif (self::verifix_static($func, $strict, $bound, $reason)) { + $type = self::TYPE_METHOD; + } else { + $valid = false; + } + } else { + if (self::verifix_static($func, $strict, $bound, $reason)) { + $type = self::TYPE_METHOD; + } elseif (self::verifix_method($func, $strict, $bound, $reason)) { + $type = self::TYPE_METHOD; + } else { + $valid = false; } } - return $c->newInstanceArgs($args); + if ($valid) return new self($type, $func, $bound, $reason); + } + throw self::not_a_callable($func, $reason); + } + + static function call($func, ...$args) { + return self::with($func)->invoke($args); + } + + ############################################################################# + + protected function __construct(int $type, $func, bool $bound=false, ?string $reason=null) { + $object = null; + $prefixArgs = []; + if (!is_array($func)) { + $reflection = $func; + $func = null; + } else { + if (count($func) > 2) { + $prefixArgs = array_slice($func, 2); + $func = array_slice($func, 0, 2); + } + [$c, $f] = $func; + switch ($type) { + case self::TYPE_FUNCTION: + $reflection = new ReflectionFunction($f); + break; + case self::TYPE_CLASS: + $reflection = new ReflectionClass($c); + break; + case self::TYPE_METHOD: + if ($c === null) { + $reflection = null; + } else { + $reflection = new ReflectionMethod($c, $f); + if (is_object($c)) $object = $c; + } + break; + default: + throw StateException::unexpected_state(); + } + } + + $this->type = $type; + $this->func = $func; + $this->bound = $bound; + $this->reason = $reason; + $this->object = $object; + $this->prefixArgs = $prefixArgs; + $this->updateReflection($reflection); + } + + protected int $type; + + protected ?array $func; + + protected bool $bound; + + protected ?string $reason; + + protected ?object $object; + + protected array $prefixArgs; + + /** @var Closure|ReflectionFunction|ReflectionMethod|ReflectionClass */ + protected $reflection; + + protected bool $variadic; + + protected int $minArgs; + + protected int $maxArgs; + + protected function updateReflection($reflection): void { + $variadic = false; + $minArgs = $maxArgs = 0; + if ($reflection instanceof Closure) { + } elseif ($reflection instanceof ReflectionClass) { + $constructor = $reflection->getConstructor(); + if ($constructor === null) { + $variadic = false; + $minArgs = $maxArgs = 0; + } else { + $variadic = $constructor->isVariadic(); + $minArgs = $constructor->getNumberOfRequiredParameters(); + $maxArgs = $constructor->getNumberOfParameters(); + } + } elseif ($reflection !== null) { + $variadic = $reflection->isVariadic(); + $minArgs = $reflection->getNumberOfRequiredParameters(); + $maxArgs = $reflection->getNumberOfParameters(); + } + $this->reflection = $reflection; + $this->variadic = $variadic; + $this->minArgs = $minArgs; + $this->maxArgs = $maxArgs; + } + + function bind($object, bool $rebind=false): self { + if ($this->type !== self::TYPE_METHOD) return $this; + $bound = $this->bound && $this->object !== null; + if ($bound && !$rebind) return $this; + [$c, $f] = $this->func; + if ($this->reflection === null) { + $this->func[0] = $c = $object; + $this->updateReflection(new ReflectionMethod($c, $f)); + } + if (is_string($c) && is_object($object) && !($object instanceof $c)) { + throw ValueException::invalid_type($object, $c); + } + if (is_object($object)) { + $this->object = $object; + $this->bound = true; + } + return $this; + } + + function invoke(?array $args=null) { + $args = array_merge($this->prefixArgs, $args ?? []); + if (!$this->variadic) $args = array_slice($args, 0, $this->maxArgs); + $minArgs = $this->minArgs; + while (count($args) < $minArgs) $args[] = null; + + switch ($this->type) { + case self::TYPE_CLOSURE: + /** @var Closure $closure */ + $closure = $this->reflection; + return $closure(...$args); + case self::TYPE_FUNCTION: + /** @var ReflectionFunction $function */ + $function = $this->reflection; + return $function->invoke(...$args); + case self::TYPE_METHOD: + /** @var ReflectionMethod $method */ + $method = $this->reflection; + if ($method === null) throw self::not_a_callable($this->func, $this->reason); + return $method->invoke($this->object, ...$args); + case self::TYPE_CLASS: + /** @var ReflectionClass $class */ + $class = $this->reflection; + return $class->newInstance(...$args); + default: + throw StateException::unexpected_state(); } } } diff --git a/src/php/mprop.php b/src/php/mprop.php index de61907..92e909f 100644 --- a/src/php/mprop.php +++ b/src/php/mprop.php @@ -44,7 +44,7 @@ class mprop { } catch (ReflectionException $e) { return oprop::get($object, $property, $default); } - return func::call([$object, $m], $default); + return nur_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); } - func::call([$object, $m], $value); + nur_func::call([$object, $m], $value); return $value; } diff --git a/wip/php/orig_func.php b/src/php/nur_func.php similarity index 70% rename from wip/php/orig_func.php rename to src/php/nur_func.php index 773d355..ed09cf6 100644 --- a/wip/php/orig_func.php +++ b/src/php/nur_func.php @@ -1,10 +1,9 @@ method" méthode à lier à un objet avant l'appel - * - ["method"] si !class_exists("method") - * - [null, "method", ...] méthode à lier à un objet avant l'appel - * - [$object, "method", ...] - * - classes - * - "class" si !function_exists("class") - * - "class::" - * - ["class"] si class_exists("class") - * - ["class", null, ...] - * - * les formes "func" et "class" sont distinguées en vérifiant l'existence de la - * fonction - * - * les formes ["class"] et ["method"] sont distinguées en vérifiant l'existence - * de la classe */ -class orig_func { - /** tester si $value est une chaine non vide */ - private static function is_ne($value): bool { - return is_string($value) && strlen($value) > 0; - } - +class nur_func { /** - * tester si $func est d'une des formes suivantes: - * - "func" si function_exists("func") - * - [false, "func", ...] + * tester si $func est une chaine de la forme "XXX::method" où 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_global($func): bool { - if (self::is_ne($func)) { + static final function is_static($func, bool $allowClass=false): bool { + if (is_string($func)) { $pos = strpos($func, "::"); - return $pos === false && function_exists($func); - } elseif (is_array($func)) { - return ($func[0] ?? null) === false - && self::is_ne($func[1] ?? null); - } - return false; - } - - static final function fix_global_args(&$func, ?array &$args): bool { - if ($args === null) $args = []; - if (is_array($func)) { - if (count($func) > 2) { - $prefix_args = array_slice($func, 2); - $func = array_slice($func, 1, 1)[0]; - $args = array_merge($prefix_args, $args); - } else { - $func = $func[0]; - } - return true; - } - return false; - } - - /** - * tester si $func est d'une des formes suivantes: - * - "::method" - * - "class::method" - * - ["method"] si !class_exists("method") - * - [anything, "method", ...] - */ - static final function is_static($func): bool { - if (self::is_ne($func)) { - $pos = strpos($func, "::"); - return $pos !== false && $pos + 2 < strlen($func); + if ($pos === false) return false; + return $pos + 2 < strlen($func); } elseif (is_array($func) && array_key_exists(0, $func)) { $count = count($func); if ($count == 1) { - return self::is_ne($func[0]) && !class_exists($func[0]); - } elseif ($count > 1 && array_key_exists(1, $func)) { - return self::is_ne($func[1]); + 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; + if (!is_string($func[1]) || strlen($func[1]) == 0) return false; + if (strpos($func[1], "\\") !== false) return false; + return true; } } return false; } /** - * en assumant que {@link self::is_static()} retourne true: - * - si $func est une chaine de la forme "::method" alors la remplacer par la + * 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 + * + * 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)) { - if (substr($func, 0, 2) == "::") { - $func = "$class$func"; - return true; - } - } else { + 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]]; @@ -130,43 +73,37 @@ class orig_func { } /** tester si $method est une chaine de la forme "->method" */ - private static function isam(&$method, bool $requireArrow=false): bool { - if (is_string($method)) { - if (substr($method, 0, 2) == "->") { - $method = substr($method, 2); - } elseif ($requireArrow) { - return false; - } - return strlen($method) > 0; - } - return false; + private static function isam($method): bool { + return is_string($method) + && strlen($method) > 2 + && substr($method, 0, 2) == "->"; } /** - * tester si $func est d'une des formes suivantes: - * - "->method" - * - ["method"] si !class_exists("method") - * - [anything, "method", ...] + * 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, true); + return self::isam($func); } elseif (is_array($func) && array_key_exists(0, $func)) { - $count = count($func); - if ($count == 1) { - return self::isam($func[0]) && !class_exists($func[0]); - } elseif ($count > 1 && array_key_exists(1, $func)) { - return self::isam($func[1]); + if (self::isam($func[0])) { + # ["->method", ...] + return true; + } + if (array_key_exists(1, $func) && self::isam($func[1])) { + # [anything, "->method", ...] + return true; } } return false; } /** - * en assumant que {@link self::is_method()} retourne true: - * - si $func est une chaine de la forme "->method" alors la remplacer par le + * 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"], + * + * 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 @@ -176,18 +113,14 @@ class orig_func { if (is_string($func)) { if (self::isam($func)) { - $func = [$object, $func]; + $func = [$object, substr($func, 2)]; return true; } - } else { - $count = count($func); - if ($count == 1) { - self::isam($func[0]); - $func = [$object, $func[0]]; - return true; - } else { + } 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; - self::isam($func[1]); + $func[1] = substr($func[1], 2); return true; } } @@ -423,32 +356,29 @@ class orig_func { } /** - * tester si $class est d'une des formes suivantes: - * - "class" si !function_exists("class") - * - "class::" - * - ["class"] si class_exists("class") - * - ["class", null, ...] + * 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 (self::is_ne($class)) { - return str::ends_with("::", $class) - || !function_exists($class); - } elseif (is_array($class) && self::is_ne($class[0] ?? null)) { - $count = count($class); - if ($count == 1) { - return class_exists($class[0]); - } elseif ($count > 1 && array_key_exists(1, $class)) { - return $class[1] === null; - } + if (is_string($class)) { + return class_exists($class); + } elseif (is_array($class) && array_key_exists(0, $class)) { + return class_exists($class[0]); } return false; } /** - * si $class est un tableau de plus de 2 éléments, alors déplacer les éléments - * supplémentaires au début de $args. par exemple: + * 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", null, "arg1", "arg2"]; + * $class = ["class", "arg1", "arg2"]; * $args = ["arg3"]; * func::fix_class_args($class, $args) * # $class === "class" @@ -460,8 +390,8 @@ class orig_func { static final function fix_class_args(&$class, ?array &$args): bool { if ($args === null) $args = []; if (is_array($class)) { - if (count($class) > 2) { - $prefix_args = array_slice($class, 2); + if (count($class) > 1) { + $prefix_args = array_slice($class, 1); $class = array_slice($class, 0, 1)[0]; $args = array_merge($prefix_args, $args); } else { diff --git a/tests/php/funcTest.php b/tests/php/funcTest.php index 3ddf2aa..9b183bd 100644 --- a/tests/php/funcTest.php +++ b/tests/php/funcTest.php @@ -1,292 +1,1143 @@ ", + false, null, + false, null, + ], + ["tsimple", + true, [false, "tsimple"], + true, [false, "tsimple"], + ], + ['nur\sery\php\impl\ntsimple', + true, [false, 'nur\sery\php\impl\ntsimple'], + true, [false, 'nur\sery\php\impl\ntsimple'], + ], + ['tmissing', + false, null, + true, [false, 'tmissing'], + ], + ["::tstatic", + false, null, + false, null, + ], + ["->tmethod", + false, null, + false, null, + ], + ["::tmissing", + false, null, + false, null, + ], + ["->tmissing", + false, null, + false, null, + ], + ["xxx::tmissing", + false, null, + false, null, + ], + ["xxx->tmissing", + false, null, + false, null, + ], + [SC::class."::tstatic", + false, null, + false, null, + ], + [SC::class."->tmethod", + false, null, + false, null, + ], + [SC::class."::tmissing", + false, null, + false, null, + ], + [SC::class."->tmissing", + false, null, + false, null, + ], + # tableaux avec un seul scalaire + [[], + false, null, + false, null, + ], + [[null], + false, null, + false, null, + ], + [[false], + false, null, + false, null, + ], + [[""], + false, null, + false, null, + ], + [["::"], + false, null, + false, null, + ], + [["->"], + false, null, + false, null, + ], + [["tsimple"], + false, null, + false, null, + ], + [['nur\sery\php\impl\ntsimple'], + false, null, + false, null, + ], + [["::tstatic"], + false, null, + false, null, + ], + [["->tmethod"], + false, null, + false, null, + ], + [["::tmissing"], + false, null, + false, null, + ], + [["->tmissing"], + false, null, + false, null, + ], + [["xxx::tmissing"], + false, null, + false, null, + ], + [["xxx->tmissing"], + false, null, + false, null, + ], + [[SC::class."::tstatic"], + false, null, + false, null, + ], + [[SC::class."->tmethod"], + false, null, + false, null, + ], + [[SC::class."::tmissing"], + false, null, + false, null, + ], + [[SC::class."->tmissing"], + false, null, + false, null, + ], + # tableaux avec deux scalaires + [[null, "tsimple"], + false, null, + false, null, + ], + [[null, 'nur\sery\php\impl\ntsimple'], + false, null, + false, null, + ], + [[null, "tmissing"], + false, null, + false, null, + ], + [[null, "::tstatic"], + false, null, + false, null, + ], + [[null, "->tmethod"], + false, null, + false, null, + ], + [[null, "::tmissing"], + false, null, + false, null, + ], + [[null, "->tmissing"], + false, null, + false, null, + ], + [[false, "tsimple"], + true, [false, "tsimple"], + true, [false, "tsimple"], + ], + [[false, 'nur\sery\php\impl\ntsimple'], + true, [false, 'nur\sery\php\impl\ntsimple'], + true, [false, 'nur\sery\php\impl\ntsimple'], + ], + [[false, "tmissing"], + false, null, + true, [false, "tmissing"], + ], + [[false, "::tstatic"], + false, null, + false, null, + ], + [[false, "->tmethod"], + false, null, + false, null, + ], + [[false, "::tmissing"], + false, null, + false, null, + ], + [[false, "->tmissing"], + false, null, + false, null, + ], + [["", "tsimple"], + false, null, + false, null, + ], + [["", 'nur\sery\php\impl\ntsimple'], + false, null, + false, null, + ], + [["", "tmissing"], + false, null, + false, null, + ], + [["", "::tstatic"], + false, null, + false, null, + ], + [["", "->tmethod"], + false, null, + false, null, + ], + [["", "::tmissing"], + false, null, + false, null, + ], + [["", "->tmissing"], + false, null, + false, null, + ], + [["xxx", "tmissing"], + false, null, + false, null, + ], + [["xxx", "::tmissing"], + false, null, + false, null, + ], + [["xxx", "->tmissing"], + false, null, + false, null, + ], + [[SC::class, "tstatic"], + false, null, + false, null, + ], + [[SC::class, "::tstatic"], + false, null, + false, null, + ], + [[SC::class, "tmethod"], + false, null, + false, null, + ], + [[SC::class, "->tmethod"], + false, null, + false, null, + ], + [[SC::class, "tmissing"], + false, null, + false, null, + ], + [[SC::class, "::tmissing"], + false, null, + false, null, + ], + [[SC::class, "->tmissing"], + false, null, + false, null, + ], + ]; - self::assertTrue(func::is_static("::xxx")); - self::assertTrue(func::is_static(["xxx"])); - self::assertTrue(func::is_static([null, "yyy"])); - self::assertTrue(func::is_static(["xxx", "yyy"])); - self::assertTrue(func::is_static([null, "yyy", "aaa"])); - self::assertTrue(func::is_static(["xxx", "yyy", "aaa"])); + function testFunction() { + foreach (self::FUNCTION_TESTS as $args) { + [$func, + $verifix1, $func1, + $verifix2, $func2, + ] = $args; + if ($func === ["", "tsimple"]) { + //echo "breakpoint"; + } + + $workf = $func; + $msg = var_export($func, true)." (strict)"; + self::assertSame($verifix1, func::verifix_function($workf, true), "$msg --> verifix"); + if ($verifix1) { + self::assertSame($func1, $workf, "$msg --> func"); + } + + $workf = $func; + $msg = var_export($func, true)." (lenient)"; + self::assertSame($verifix2, func::verifix_function($workf, false), "$msg --> verifix"); + if ($verifix2) { + self::assertSame($func2, $workf, "$msg --> func"); + } + } + } + + const STATIC_TESTS = [ + # scalaires + [null, + false, null, null, + false, null, null, + ], + [false, + false, null, null, + false, null, null, + ], + ["", + false, null, null, + false, null, null, + ], + ["::", + false, null, null, + false, null, null, + ], + ["->", + false, null, null, + false, null, null, + ], + ["tsimple", + false, null, null, + false, null, null, + ], + ['nur\sery\php\impl\ntsimple', + false, null, null, + false, null, null, + ], + ['tmissing', + false, null, null, + false, null, null, + ], + ["::tstatic", + true, false, [null, "tstatic"], + true, false, [null, "tstatic"], + ], + ["->tmethod", + false, null, null, + false, null, null, + ], + ["::tmissing", + true, false, [null, "tmissing"], + true, false, [null, "tmissing"], + ], + ["->tmissing", + false, null, null, + false, null, null, + ], + ["xxx::tmissing", + false, null, null, + true, true, ["xxx", "tmissing"], + ], + ["xxx->tmissing", + false, null, null, + false, null, null, + ], + [SC::class."::tstatic", + true, true, [SC::class, "tstatic"], + true, true, [SC::class, "tstatic"], + ], + [SC::class."->tmethod", + false, null, null, + false, null, null, + ], + [SC::class."::tmissing", + false, null, null, + true, true, [SC::class, "tmissing"], + ], + [SC::class."->tmissing", + false, null, null, + false, null, null, + ], + # tableaux avec un seul scalaire + [[], + false, null, null, + false, null, null, + ], + [[null], + false, null, null, + false, null, null, + ], + [[false], + false, null, null, + false, null, null, + ], + [[""], + false, null, null, + false, null, null, + ], + [["::"], + false, null, null, + false, null, null, + ], + [["->"], + false, null, null, + false, null, null, + ], + [["tsimple"], + false, null, null, + false, null, null, + ], + [['nur\sery\php\impl\ntsimple'], + false, null, null, + false, null, null, + ], + [["::tstatic"], + true, false, [null, "tstatic"], + true, false, [null, "tstatic"], + ], + [["->tmethod"], + false, null, null, + false, null, null, + ], + [["::tmissing"], + true, false, [null, "tmissing"], + true, false, [null, "tmissing"], + ], + [["->tmissing"], + false, null, null, + false, null, null, + ], + [["xxx::tmissing"], + false, null, null, + true, true, ["xxx", "tmissing"], + ], + [["xxx->tmissing"], + false, null, null, + false, null, null, + ], + [[SC::class."::tstatic"], + true, true, [SC::class, "tstatic"], + true, true, [SC::class, "tstatic"], + ], + [[SC::class."->tmethod"], + false, null, null, + false, null, null, + ], + [[SC::class."::tmissing"], + false, null, null, + true, true, [SC::class, "tmissing"], + ], + [[SC::class."->tmissing"], + false, null, null, + false, null, null, + ], + # tableaux avec deux scalaires + [[null, "tsimple"], + true, false, [null, "tsimple"], + true, false, [null, "tsimple"], + ], + [[null, 'nur\sery\php\impl\ntsimple'], + false, null, null, + false, null, null, + ], + [[null, "tmissing"], + true, false, [null, "tmissing"], + true, false, [null, "tmissing"], + ], + [[null, "::tstatic"], + true, false, [null, "tstatic"], + true, false, [null, "tstatic"], + ], + [[null, "->tmethod"], + false, null, null, + false, null, null, + ], + [[null, "::tmissing"], + true, false, [null, "tmissing"], + true, false, [null, "tmissing"], + ], + [[null, "->tmissing"], + false, null, null, + false, null, null, + ], + [[false, "tsimple"], + false, null, null, + false, null, null, + ], + [[false, 'nur\sery\php\impl\ntsimple'], + false, null, null, + false, null, null, + ], + [[false, "tmissing"], + false, null, null, + false, null, null, + ], + [[false, "::tstatic"], + false, null, null, + false, null, null, + ], + [[false, "->tmethod"], + false, null, null, + false, null, null, + ], + [[false, "::tmissing"], + false, null, null, + false, null, null, + ], + [[false, "->tmissing"], + false, null, null, + false, null, null, + ], + [["", "tsimple"], + false, null, null, + false, null, null, + ], + [["", 'nur\sery\php\impl\ntsimple'], + false, null, null, + false, null, null, + ], + [["", "tmissing"], + false, null, null, + false, null, null, + ], + [["", "::tstatic"], + false, null, null, + false, null, null, + ], + [["", "->tmethod"], + false, null, null, + false, null, null, + ], + [["", "::tmissing"], + false, null, null, + false, null, null, + ], + [["", "->tmissing"], + false, null, null, + false, null, null, + ], + [["xxx", "tmissing"], + false, null, null, + true, true, ["xxx", "tmissing"], + ], + [["xxx", "::tmissing"], + false, null, null, + true, true, ["xxx", "tmissing"], + ], + [["xxx", "->tmissing"], + false, null, null, + false, null, null, + ], + [[SC::class, "tstatic"], + true, true, [SC::class, "tstatic"], + true, true, [SC::class, "tstatic"], + ], + [[SC::class, "::tstatic"], + true, true, [SC::class, "tstatic"], + true, true, [SC::class, "tstatic"], + ], + [[SC::class, "tmethod"], + true, true, [SC::class, "tmethod"], + true, true, [SC::class, "tmethod"], + ], + [[SC::class, "->tmethod"], + false, null, null, + false, null, null, + ], + [[SC::class, "tmissing"], + false, null, null, + true, true, [SC::class, "tmissing"], + ], + [[SC::class, "::tmissing"], + false, null, null, + true, true, [SC::class, "tmissing"], + ], + [[SC::class, "->tmissing"], + false, null, null, + false, null, null, + ], + ]; + + function testStatic() { + foreach (self::STATIC_TESTS as $args) { + [$func, + $verifix1, $bound1, $func1, + $verifix2, $bound2, $func2, + ] = $args; + if ($func === ["", "tsimple"]) { + //echo "breakpoint"; + } + + $workf = $func; + $msg = var_export($func, true)." (strict)"; + self::assertSame($verifix1, func::verifix_static($workf, true, $bound), "$msg --> verifix"); + if ($verifix1) { + self::assertSame($bound1, $bound, "$msg --> bound"); + self::assertSame($func1, $workf, "$msg --> func"); + } + + $workf = $func; + $msg = var_export($func, true)." (lenient)"; + self::assertSame($verifix2, func::verifix_static($workf, false, $bound), "$msg --> verifix"); + if ($verifix2) { + self::assertSame($bound2, $bound, "$msg --> bound"); + self::assertSame($func2, $workf, "$msg --> func"); + } + } + } + + const METHOD_TESTS = [ + # scalaires + [null, + false, null, null, + false, null, null, + ], + [false, + false, null, null, + false, null, null, + ], + ["", + false, null, null, + false, null, null, + ], + ["::", + false, null, null, + false, null, null, + ], + ["->", + false, null, null, + false, null, null, + ], + ["tsimple", + false, null, null, + false, null, null, + ], + ['nur\sery\php\impl\ntsimple', + false, null, null, + false, null, null, + ], + ['tmissing', + false, null, null, + false, null, null, + ], + ["::tstatic", + false, null, null, + false, null, null, + ], + ["->tmethod", + true, false, [null, "tmethod"], + true, false, [null, "tmethod"], + ], + ["::tmissing", + false, null, null, + false, null, null, + ], + ["->tmissing", + true, false, [null, "tmissing"], + true, false, [null, "tmissing"], + ], + ["xxx::tmissing", + false, null, null, + false, null, null, + ], + ["xxx->tmissing", + false, null, null, + true, true, ["xxx", "tmissing"], + ], + [SC::class."::tstatic", + false, null, null, + false, null, null, + ], + [SC::class."->tmethod", + true, true, [SC::class, "tmethod"], + true, true, [SC::class, "tmethod"], + ], + [SC::class."::tmissing", + false, null, null, + false, null, null, + ], + [SC::class."->tmissing", + false, null, null, + true, true, [SC::class, "tmissing"], + ], + # tableaux avec un seul scalaire + [[], + false, null, null, + false, null, null, + ], + [[null], + false, null, null, + false, null, null, + ], + [[false], + false, null, null, + false, null, null, + ], + [[""], + false, null, null, + false, null, null, + ], + [["::"], + false, null, null, + false, null, null, + ], + [["->"], + false, null, null, + false, null, null, + ], + [["tsimple"], + false, null, null, + false, null, null, + ], + [['nur\sery\php\impl\ntsimple'], + false, null, null, + false, null, null, + ], + [["::tstatic"], + false, null, null, + false, null, null, + ], + [["->tmethod"], + true, false, [null, "tmethod"], + true, false, [null, "tmethod"], + ], + [["::tmissing"], + false, null, null, + false, null, null, + ], + [["->tmissing"], + true, false, [null, "tmissing"], + true, false, [null, "tmissing"], + ], + [["xxx::tmissing"], + false, null, null, + false, null, null, + ], + [["xxx->tmissing"], + false, null, null, + true, true, ["xxx", "tmissing"], + ], + [[SC::class."::tstatic"], + false, null, null, + false, null, null, + ], + [[SC::class."->tmethod"], + true, true, [SC::class, "tmethod"], + true, true, [SC::class, "tmethod"], + ], + [[SC::class."::tmissing"], + false, null, null, + false, null, null, + ], + [[SC::class."->tmissing"], + false, null, null, + true, true, [SC::class, "tmissing"], + ], + # tableaux avec deux scalaires + [[null, "tsimple"], + true, false, [null, "tsimple"], + true, false, [null, "tsimple"], + ], + [[null, 'nur\sery\php\impl\ntsimple'], + false, null, null, + false, null, null, + ], + [[null, "tmissing"], + true, false, [null, "tmissing"], + true, false, [null, "tmissing"], + ], + [[null, "::tstatic"], + false, null, null, + false, null, null, + ], + [[null, "->tmethod"], + true, false, [null, "tmethod"], + true, false, [null, "tmethod"], + ], + [[null, "::tmissing"], + false, null, null, + false, null, null, + ], + [[null, "->tmissing"], + true, false, [null, "tmissing"], + true, false, [null, "tmissing"], + ], + [[false, "tsimple"], + false, null, null, + false, null, null, + ], + [[false, 'nur\sery\php\impl\ntsimple'], + false, null, null, + false, null, null, + ], + [[false, "tmissing"], + false, null, null, + false, null, null, + ], + [[false, "::tstatic"], + false, null, null, + false, null, null, + ], + [[false, "->tmethod"], + false, null, null, + false, null, null, + ], + [[false, "::tmissing"], + false, null, null, + false, null, null, + ], + [[false, "->tmissing"], + false, null, null, + false, null, null, + ], + [["", "tsimple"], + false, null, null, + false, null, null, + ], + [["", 'nur\sery\php\impl\ntsimple'], + false, null, null, + false, null, null, + ], + [["", "tmissing"], + false, null, null, + false, null, null, + ], + [["", "::tstatic"], + false, null, null, + false, null, null, + ], + [["", "->tmethod"], + false, null, null, + false, null, null, + ], + [["", "::tmissing"], + false, null, null, + false, null, null, + ], + [["", "->tmissing"], + false, null, null, + false, null, null, + ], + [["xxx", "tmissing"], + false, null, null, + true, true, ["xxx", "tmissing"], + ], + [["xxx", "::tmissing"], + false, null, null, + false, null, null, + ], + [["xxx", "->tmissing"], + false, null, null, + true, true, ["xxx", "tmissing"], + ], + [[SC::class, "tstatic"], + true, true, [SC::class, "tstatic"], + true, true, [SC::class, "tstatic"], + ], + [[SC::class, "::tstatic"], + false, null, null, + false, null, null, + ], + [[SC::class, "tmethod"], + true, true, [SC::class, "tmethod"], + true, true, [SC::class, "tmethod"], + ], + [[SC::class, "->tmethod"], + true, true, [SC::class, "tmethod"], + true, true, [SC::class, "tmethod"], + ], + [[SC::class, "tmissing"], + false, null, null, + true, true, [SC::class, "tmissing"], + ], + [[SC::class, "::tmissing"], + false, null, null, + false, null, null, + ], + [[SC::class, "->tmissing"], + false, null, null, + true, true, [SC::class, "tmissing"], + ], + ]; + + function testMethod() { + foreach (self::METHOD_TESTS as $args) { + [$func, + $verifix1, $bound1, $func1, + $verifix2, $bound2, $func2, + ] = $args; + + $workf = $func; + $msg = var_export($func, true)." (strict)"; + self::assertSame($verifix1, func::verifix_method($workf, true, $bound), "$msg --> verifix"); + if ($verifix1) { + self::assertSame($bound1, $bound, "$msg --> bound"); + self::assertSame($func1, $workf, "$msg --> func"); + } + + $workf = $func; + $msg = var_export($func, true)." (lenient)"; + self::assertSame($verifix2, func::verifix_method($workf, false, $bound), "$msg --> verifix"); + if ($verifix2) { + self::assertSame($bound2, $bound, "$msg --> bound"); + self::assertSame($func2, $workf, "$msg --> func"); + } + } + } + + function testInvokeFunction() { + # m1 + self::assertSame([null], func::call("tm1")); + self::assertSame([null], func::call("tm1", null)); + self::assertSame([null], func::call("tm1", null, null)); + self::assertSame([null], func::call("tm1", null, null, null)); + self::assertSame([null], func::call("tm1", null, null, null, null)); + self::assertSame([1], func::call("tm1", 1)); + self::assertSame([1], func::call("tm1", 1, 2)); + self::assertSame([1], func::call("tm1", 1, 2, 3)); + self::assertSame([1], func::call("tm1", 1, 2, 3, 4)); + + # o1 + self::assertSame([9], func::call("to1")); + self::assertSame([null], func::call("to1", null)); + self::assertSame([null], func::call("to1", null, null)); + self::assertSame([null], func::call("to1", null, null, null)); + self::assertSame([null], func::call("to1", null, null, null, null)); + self::assertSame([1], func::call("to1", 1)); + self::assertSame([1], func::call("to1", 1, 2)); + self::assertSame([1], func::call("to1", 1, 2, 3)); + self::assertSame([1], func::call("to1", 1, 2, 3, 4)); + + # v + self::assertSame([], func::call("tv")); + self::assertSame([null], func::call("tv", null)); + self::assertSame([null, null], func::call("tv", null, null)); + self::assertSame([null, null, null], func::call("tv", null, null, null)); + self::assertSame([null, null, null, null], func::call("tv", null, null, null, null)); + self::assertSame([1], func::call("tv", 1)); + self::assertSame([1, 2], func::call("tv", 1, 2)); + self::assertSame([1, 2, 3], func::call("tv", 1, 2, 3)); + self::assertSame([1, 2, 3, 4], func::call("tv", 1, 2, 3, 4)); + + # m1o1 + self::assertSame([null, 9], func::call("tm1o1")); + self::assertSame([null, 9], func::call("tm1o1", null)); + self::assertSame([null, null], func::call("tm1o1", null, null)); + self::assertSame([null, null], func::call("tm1o1", null, null, null)); + self::assertSame([null, null], func::call("tm1o1", null, null, null, null)); + self::assertSame([1, 9], func::call("tm1o1", 1)); + self::assertSame([1, 2], func::call("tm1o1", 1, 2)); + self::assertSame([1, 2], func::call("tm1o1", 1, 2, 3)); + self::assertSame([1, 2], func::call("tm1o1", 1, 2, 3, 4)); + + # m1v + self::assertSame([null], func::call("tm1v")); + self::assertSame([null], func::call("tm1v", null)); + self::assertSame([null, null], func::call("tm1v", null, null)); + self::assertSame([null, null, null], func::call("tm1v", null, null, null)); + self::assertSame([null, null, null, null], func::call("tm1v", null, null, null, null)); + self::assertSame([1], func::call("tm1v", 1)); + self::assertSame([1, 2], func::call("tm1v", 1, 2)); + self::assertSame([1, 2, 3], func::call("tm1v", 1, 2, 3)); + self::assertSame([1, 2, 3, 4], func::call("tm1v", 1, 2, 3, 4)); + + # m1o1v + self::assertSame([null, 9], func::call("tm1o1v")); + self::assertSame([null, 9], func::call("tm1o1v", null)); + self::assertSame([null, null], func::call("tm1o1v", null, null)); + self::assertSame([null, null, null], func::call("tm1o1v", null, null, null)); + self::assertSame([null, null, null, null], func::call("tm1o1v", null, null, null, null)); + self::assertSame([1, 9], func::call("tm1o1v", 1)); + self::assertSame([1, 2], func::call("tm1o1v", 1, 2)); + self::assertSame([1, 2, 3], func::call("tm1o1v", 1, 2, 3)); + self::assertSame([1, 2, 3, 4], func::call("tm1o1v", 1, 2, 3, 4)); + + # o1v + self::assertSame([9], func::call("to1v")); + self::assertSame([null], func::call("to1v", null)); + self::assertSame([null, null], func::call("to1v", null, null)); + self::assertSame([null, null, null], func::call("to1v", null, null, null)); + self::assertSame([null, null, null, null], func::call("to1v", null, null, null, null)); + self::assertSame([1], func::call("to1v", 1)); + self::assertSame([1, 2], func::call("to1v", 1, 2)); + self::assertSame([1, 2, 3], func::call("to1v", 1, 2, 3)); + self::assertSame([1, 2, 3, 4], func::call("to1v", 1, 2, 3, 4)); + } + + function testInvokeClass() { + $func = func::with(SC::class); + self::assertInstanceOf(SC::class, $func->invoke()); + self::assertInstanceOf(SC::class, $func->invoke([])); + self::assertInstanceOf(SC::class, $func->invoke([1])); + self::assertInstanceOf(SC::class, $func->invoke([1, 2])); + self::assertInstanceOf(SC::class, $func->invoke([1, 2, 3])); + + $func = func::with(C0::class); + self::assertInstanceOf(C0::class, $func->invoke()); + self::assertInstanceOf(C0::class, $func->invoke([])); + self::assertInstanceOf(C0::class, $func->invoke([1])); + self::assertInstanceOf(C0::class, $func->invoke([1, 2])); + self::assertInstanceOf(C0::class, $func->invoke([1, 2, 3])); + + $func = func::with(C1::class); + /** @var C1 $i1 */ + $i1 = $func->invoke(); + self::assertInstanceOf(C1::class, $i1); self::assertSame(null, $i1->first); + $i1 = $func->invoke([]); + self::assertInstanceOf(C1::class, $i1); self::assertSame(null, $i1->first); + $i1 = $func->invoke([1]); + self::assertInstanceOf(C1::class, $i1); self::assertSame(1, $i1->first); + $i1 = $func->invoke([1, 2]); + self::assertInstanceOf(C1::class, $i1); self::assertSame(1, $i1->first); } - function testFix_static() { - $class = "class"; - $func = "::xxx"; - func::fix_static($func, $class); - self::assertSame("class::xxx", $func); - $func = ["xxx"]; - func::fix_static($func, $class); - self::assertSame(["class", "xxx"], $func); - $func = [null, "yyy"]; - func::fix_static($func, $class); - self::assertSame(["class", "yyy"], $func); - $func = ["xxx", "yyy"]; - func::fix_static($func, $class); - self::assertSame(["xxx", "yyy"], $func); - $func = [null, "yyy", "aaa"]; - func::fix_static($func, $class); - self::assertSame(["class", "yyy", "aaa"], $func); - $func = ["xxx", "yyy", "aaa"]; - func::fix_static($func, $class); - self::assertSame(["xxx", "yyy", "aaa"], $func); - } - - function testIs_method() { - self::assertFalse(func::is_method(null)); - self::assertFalse(func::is_method("")); - self::assertFalse(func::is_method("->")); - self::assertFalse(func::is_method([])); - self::assertFalse(func::is_method([""])); - self::assertFalse(func::is_method([null, "->"])); - self::assertFalse(func::is_method(["xxx", "->"])); - - self::assertTrue(func::is_method("->xxx")); - self::assertTrue(func::is_method(["->xxx"])); - self::assertTrue(func::is_method([null, "->yyy"])); - self::assertTrue(func::is_method(["xxx", "->yyy"])); - self::assertTrue(func::is_method([null, "->yyy", "aaa"])); - self::assertTrue(func::is_method(["xxx", "->yyy", "aaa"])); - } - - function testFix_method() { - $object = new \stdClass(); - $func= "->xxx"; - func::fix_method($func, $object); - self::assertSame([$object, "xxx"], $func); - $func= ["->xxx"]; - func::fix_method($func, $object); - self::assertSame([$object, "xxx"], $func); - $func= [null, "->yyy"]; - func::fix_method($func, $object); - self::assertSame([$object, "yyy"], $func); - $func= ["xxx", "->yyy"]; - func::fix_method($func, $object); - self::assertSame([$object, "yyy"], $func); - $func= [null, "->yyy", "aaa"]; - func::fix_method($func, $object); - self::assertSame([$object, "yyy", "aaa"], $func); - $func= ["xxx", "->yyy", "aaa"]; - func::fix_method($func, $object); - self::assertSame([$object, "yyy", "aaa"], $func); - } - - function testCall() { - self::assertSame(36, func::call("func36")); - self::assertSame(12, func::call(TC::class."::method")); - self::assertSame(12, func::call([TC::class, "method"])); - $closure = function() { - return 21; + private static function invoke_asserts(): array { + $inv_ok = function($func) { + return func::with($func)->invoke(); }; - self::assertSame(21, func::call($closure)); + $inv_ko = function($func) use ($inv_ok) { + return function() use ($func, $inv_ok) { + return $inv_ok($func); + }; + }; + $bind_ok = function($func, $objet) { + return func::with($func)->bind($objet)->invoke(); + }; + $bind_ko = function($func, $object) use ($bind_ok) { + return function() use ($func, $object, $bind_ok) { + return $bind_ok($func, $object); + }; + }; + return [$inv_ok, $inv_ko, $bind_ok, $bind_ko]; } - 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], func::call("func_m1")); - self::assertSame([null], func::call("func_m1", null)); - self::assertSame([null], func::call("func_m1", null, null)); - self::assertSame([null], func::call("func_m1", null, null, null)); - self::assertSame([null], func::call("func_m1", null, null, null, null)); - self::assertSame([1], func::call("func_m1", 1)); - self::assertSame([1], func::call("func_m1", 1, 2)); - self::assertSame([1], func::call("func_m1", 1, 2, 3)); - self::assertSame([1], func::call("func_m1", 1, 2, 3, 4)); - - # o1 - self::assertSame([9], func::call("func_o1")); - self::assertSame([null], func::call("func_o1", null)); - self::assertSame([null], func::call("func_o1", null, null)); - self::assertSame([null], func::call("func_o1", null, null, null)); - self::assertSame([null], func::call("func_o1", null, null, null, null)); - self::assertSame([1], func::call("func_o1", 1)); - self::assertSame([1], func::call("func_o1", 1, 2)); - self::assertSame([1], func::call("func_o1", 1, 2, 3)); - self::assertSame([1], func::call("func_o1", 1, 2, 3, 4)); - - # v - self::assertSame([], func::call("func_v")); - self::assertSame([null], func::call("func_v", null)); - self::assertSame([null, null], func::call("func_v", null, null)); - self::assertSame([null, null, null], func::call("func_v", null, null, null)); - self::assertSame([null, null, null, null], func::call("func_v", null, null, null, null)); - self::assertSame([1], func::call("func_v", 1)); - self::assertSame([1, 2], func::call("func_v", 1, 2)); - self::assertSame([1, 2, 3], func::call("func_v", 1, 2, 3)); - self::assertSame([1, 2, 3, 4], func::call("func_v", 1, 2, 3, 4)); - - # m1o1 - self::assertSame([null, 9], func::call("func_m1o1")); - self::assertSame([null, 9], func::call("func_m1o1", null)); - self::assertSame([null, null], func::call("func_m1o1", null, null)); - self::assertSame([null, null], func::call("func_m1o1", null, null, null)); - self::assertSame([null, null], func::call("func_m1o1", null, null, null, null)); - self::assertSame([1, 9], func::call("func_m1o1", 1)); - self::assertSame([1, 2], func::call("func_m1o1", 1, 2)); - self::assertSame([1, 2], func::call("func_m1o1", 1, 2, 3)); - self::assertSame([1, 2], func::call("func_m1o1", 1, 2, 3, 4)); - - # m1v - self::assertSame([null], func::call("func_m1v")); - self::assertSame([null], func::call("func_m1v", null)); - self::assertSame([null, null], func::call("func_m1v", null, null)); - self::assertSame([null, null, null], func::call("func_m1v", null, null, null)); - self::assertSame([null, null, null, null], func::call("func_m1v", null, null, null, null)); - self::assertSame([1], func::call("func_m1v", 1)); - self::assertSame([1, 2], func::call("func_m1v", 1, 2)); - self::assertSame([1, 2, 3], func::call("func_m1v", 1, 2, 3)); - self::assertSame([1, 2, 3, 4], func::call("func_m1v", 1, 2, 3, 4)); - - # m1o1v - self::assertSame([null, 9], func::call("func_m1o1v")); - self::assertSame([null, 9], func::call("func_m1o1v", null)); - self::assertSame([null, null], func::call("func_m1o1v", null, null)); - self::assertSame([null, null, null], func::call("func_m1o1v", null, null, null)); - self::assertSame([null, null, null, null], func::call("func_m1o1v", null, null, null, null)); - self::assertSame([1, 9], func::call("func_m1o1v", 1)); - self::assertSame([1, 2], func::call("func_m1o1v", 1, 2)); - self::assertSame([1, 2, 3], func::call("func_m1o1v", 1, 2, 3)); - self::assertSame([1, 2, 3, 4], func::call("func_m1o1v", 1, 2, 3, 4)); - - # o1v - self::assertSame([9], func::call("func_o1v")); - self::assertSame([null], func::call("func_o1v", null)); - self::assertSame([null, null], func::call("func_o1v", null, null)); - self::assertSame([null, null, null], func::call("func_o1v", null, null, null)); - self::assertSame([null, null, null, null], func::call("func_o1v", null, null, null, null)); - self::assertSame([1], func::call("func_o1v", 1)); - self::assertSame([1, 2], func::call("func_o1v", 1, 2)); - self::assertSame([1, 2, 3], func::call("func_o1v", 1, 2, 3)); - self::assertSame([1, 2, 3, 4], func::call("func_o1v", 1, 2, 3, 4)); + function testInvokeStatic() { + [$inv_ok, $inv_ko, $bind_ok, $bind_ko] = self::invoke_asserts(); + $sc = new SC(); + + self::assertSame(10, $inv_ok([SC::class, "tstatic"])); + self::assertSame(10, $inv_ok([SC::class, "::tstatic"])); + self::assertSame(10, $inv_ok([SC::class, "->tstatic"])); + + self::assertSame(10, $inv_ok([$sc, "tstatic"])); + self::assertSame(10, $inv_ok([$sc, "::tstatic"])); + self::assertSame(10, $inv_ok([$sc, "->tstatic"])); + + self::assertException(ValueException::class, $inv_ko([null, "tstatic"])); + self::assertException(ValueException::class, $inv_ko([null, "::tstatic"])); + self::assertException(ValueException::class, $inv_ko([null, "->tstatic"])); + + self::assertSame(10, $bind_ok([null, "tstatic"], SC::class)); + self::assertSame(10, $bind_ok([null, "::tstatic"], SC::class)); + self::assertSame(10, $bind_ok([null, "->tstatic"], SC::class)); + + self::assertSame(10, $bind_ok([null, "tstatic"], $sc)); + self::assertSame(10, $bind_ok([null, "::tstatic"], $sc)); + self::assertSame(10, $bind_ok([null, "->tstatic"], $sc)); } - function testCall_all() { - $c1 = new C1(); - $c2 = new C2(); - $c3 = new C3(); + function testInvokeMethod() { + [$inv_ok, $inv_ko, $bind_ok, $bind_ko] = self::invoke_asserts(); + $sc = new SC(); - self::assertSameValues([11, 12], func::call_all(C1::class)); - self::assertSameValues([11, 12, 21, 22], func::call_all($c1)); - self::assertSameValues([13, 11, 12], func::call_all(C2::class)); - self::assertSameValues([13, 23, 11, 12, 21, 22], func::call_all($c2)); - self::assertSameValues([111, 13, 12], func::call_all(C3::class)); - self::assertSameValues([111, 121, 13, 23, 12, 22], func::call_all($c3)); + self::assertException(ReflectionException::class, $inv_ko([SC::class, "tmethod"])); + self::assertException(ReflectionException::class, $inv_ko([SC::class, "::tmethod"])); + self::assertException(ReflectionException::class, $inv_ko([SC::class, "->tmethod"])); - $options = "conf"; - self::assertSameValues([11], func::call_all(C1::class, $options)); - self::assertSameValues([11, 21], func::call_all($c1, $options)); - self::assertSameValues([11], func::call_all(C2::class, $options)); - self::assertSameValues([11, 21], func::call_all($c2, $options)); - self::assertSameValues([111], func::call_all(C3::class, $options)); - self::assertSameValues([111, 121], func::call_all($c3, $options)); + self::assertSame(11, $inv_ok([$sc, "tmethod"])); + self::assertException(ReflectionException::class, $inv_ko([$sc, "::tmethod"])); + self::assertSame(11, $inv_ok([$sc, "->tmethod"])); - $options = ["prefix" => "conf"]; - self::assertSameValues([11], func::call_all(C1::class, $options)); - self::assertSameValues([11, 21], func::call_all($c1, $options)); - self::assertSameValues([11], func::call_all(C2::class, $options)); - self::assertSameValues([11, 21], func::call_all($c2, $options)); - self::assertSameValues([111], func::call_all(C3::class, $options)); - self::assertSameValues([111, 121], func::call_all($c3, $options)); + self::assertException(ValueException::class, $inv_ko([null, "tmethod"])); + self::assertException(ValueException::class, $inv_ko([null, "::tmethod"])); + self::assertException(ValueException::class, $inv_ko([null, "->tmethod"])); - self::assertSameValues([11, 12], func::call_all($c1, ["include" => "x"])); - self::assertSameValues([11, 21], func::call_all($c1, ["include" => "y"])); - self::assertSameValues([11, 12, 21], func::call_all($c1, ["include" => ["x", "y"]])); + self::assertException(ReflectionException::class, $bind_ko([null, "tmethod"], SC::class)); + self::assertException(ReflectionException::class, $bind_ko([null, "::tmethod"], SC::class)); + self::assertException(ReflectionException::class, $bind_ko([null, "->tmethod"], SC::class)); - self::assertSameValues([21, 22], func::call_all($c1, ["exclude" => "x"])); - self::assertSameValues([12, 22], func::call_all($c1, ["exclude" => "y"])); - self::assertSameValues([22], func::call_all($c1, ["exclude" => ["x", "y"]])); + self::assertSame(11, $bind_ok([null, "tmethod"], $sc)); + self::assertSame(11, $bind_ok([null, "::tmethod"], $sc)); + self::assertSame(11, $bind_ok([null, "->tmethod"], $sc)); + } + } +} - self::assertSameValues([12], func::call_all($c1, ["include" => "x", "exclude" => "y"])); +namespace { + function tsimple(): int { return 0; } + function tm1($a): array { return [$a]; } + function to1($b=9): array { return [$b]; } + function tv(...$c): array { return [...$c]; } + function tm1o1($a, $b=9): array { return [$a, $b]; } + function tm1v($a, ...$c): array { return [$a, ...$c]; } + function tm1o1v($a, $b=9, ...$c): array { return [$a, $b, ...$c]; } + function to1v($b=9, ...$c): array { return [$b, ...$c]; } +} + +namespace nur\sery\php\impl { + function ntsimple(): int { return 0; } + + class SC { + static function tstatic(): int { + return 10; } - function testCons() { - $obj1 = func::cons(WoCons::class, 1, 2, 3); - self::assertInstanceOf(WoCons::class, $obj1); - - $obj2 = func::cons(WithEmptyCons::class, 1, 2, 3); - self::assertInstanceOf(WithEmptyCons::class, $obj2); - - $obj3 = func::cons(WithCons::class, 1, 2, 3); - self::assertInstanceOf(WithCons::class, $obj3); - self::assertSame(1, $obj3->first); + function tmethod(): int { + return 11; } } - class WoCons { - } - class WithEmptyCons { + class C0 { function __construct() { } - } - class WithCons { - public $first; - function __construct($first) { - $this->first = $first; - } - } - class TC { - static function method() { - return 12; + static function tstatic(): int { + return 10; + } + + function tmethod(): int { + return 11; } } class C1 { - static function confps1_xy() { + function __construct($first) { + $this->first = $first; + } + + public $first; + + static function tstatic(): int { + return 10; + } + + function tmethod(): int { 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; - } } } diff --git a/tests/php/nur_funcTest.php b/tests/php/nur_funcTest.php new file mode 100644 index 0000000..8f6dc38 --- /dev/null +++ b/tests/php/nur_funcTest.php @@ -0,0 +1,292 @@ +")); + 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; + } + } +} diff --git a/tests/wip/php/funcTest.php b/tests/wip/php/funcTest.php deleted file mode 100644 index 4b8ade1..0000000 --- a/tests/wip/php/funcTest.php +++ /dev/null @@ -1,1146 +0,0 @@ -", - false, null, - false, null, - ], - ["tsimple", - true, [false, "tsimple"], - true, [false, "tsimple"], - ], - ['nur\sery\wip\php\impl\ntsimple', - true, [false, 'nur\sery\wip\php\impl\ntsimple'], - true, [false, 'nur\sery\wip\php\impl\ntsimple'], - ], - ['tmissing', - false, null, - true, [false, 'tmissing'], - ], - ["::tstatic", - false, null, - false, null, - ], - ["->tmethod", - false, null, - false, null, - ], - ["::tmissing", - false, null, - false, null, - ], - ["->tmissing", - false, null, - false, null, - ], - ["xxx::tmissing", - false, null, - false, null, - ], - ["xxx->tmissing", - false, null, - false, null, - ], - [SC::class."::tstatic", - false, null, - false, null, - ], - [SC::class."->tmethod", - false, null, - false, null, - ], - [SC::class."::tmissing", - false, null, - false, null, - ], - [SC::class."->tmissing", - false, null, - false, null, - ], - # tableaux avec un seul scalaire - [[], - false, null, - false, null, - ], - [[null], - false, null, - false, null, - ], - [[false], - false, null, - false, null, - ], - [[""], - false, null, - false, null, - ], - [["::"], - false, null, - false, null, - ], - [["->"], - false, null, - false, null, - ], - [["tsimple"], - false, null, - false, null, - ], - [['nur\sery\wip\php\impl\ntsimple'], - false, null, - false, null, - ], - [["::tstatic"], - false, null, - false, null, - ], - [["->tmethod"], - false, null, - false, null, - ], - [["::tmissing"], - false, null, - false, null, - ], - [["->tmissing"], - false, null, - false, null, - ], - [["xxx::tmissing"], - false, null, - false, null, - ], - [["xxx->tmissing"], - false, null, - false, null, - ], - [[SC::class."::tstatic"], - false, null, - false, null, - ], - [[SC::class."->tmethod"], - false, null, - false, null, - ], - [[SC::class."::tmissing"], - false, null, - false, null, - ], - [[SC::class."->tmissing"], - false, null, - false, null, - ], - # tableaux avec deux scalaires - [[null, "tsimple"], - false, null, - false, null, - ], - [[null, 'nur\sery\wip\php\impl\ntsimple'], - false, null, - false, null, - ], - [[null, "tmissing"], - false, null, - false, null, - ], - [[null, "::tstatic"], - false, null, - false, null, - ], - [[null, "->tmethod"], - false, null, - false, null, - ], - [[null, "::tmissing"], - false, null, - false, null, - ], - [[null, "->tmissing"], - false, null, - false, null, - ], - [[false, "tsimple"], - true, [false, "tsimple"], - true, [false, "tsimple"], - ], - [[false, 'nur\sery\wip\php\impl\ntsimple'], - true, [false, 'nur\sery\wip\php\impl\ntsimple'], - true, [false, 'nur\sery\wip\php\impl\ntsimple'], - ], - [[false, "tmissing"], - false, null, - true, [false, "tmissing"], - ], - [[false, "::tstatic"], - false, null, - false, null, - ], - [[false, "->tmethod"], - false, null, - false, null, - ], - [[false, "::tmissing"], - false, null, - false, null, - ], - [[false, "->tmissing"], - false, null, - false, null, - ], - [["", "tsimple"], - false, null, - false, null, - ], - [["", 'nur\sery\wip\php\impl\ntsimple'], - false, null, - false, null, - ], - [["", "tmissing"], - false, null, - false, null, - ], - [["", "::tstatic"], - false, null, - false, null, - ], - [["", "->tmethod"], - false, null, - false, null, - ], - [["", "::tmissing"], - false, null, - false, null, - ], - [["", "->tmissing"], - false, null, - false, null, - ], - [["xxx", "tmissing"], - false, null, - false, null, - ], - [["xxx", "::tmissing"], - false, null, - false, null, - ], - [["xxx", "->tmissing"], - false, null, - false, null, - ], - [[SC::class, "tstatic"], - false, null, - false, null, - ], - [[SC::class, "::tstatic"], - false, null, - false, null, - ], - [[SC::class, "tmethod"], - false, null, - false, null, - ], - [[SC::class, "->tmethod"], - false, null, - false, null, - ], - [[SC::class, "tmissing"], - false, null, - false, null, - ], - [[SC::class, "::tmissing"], - false, null, - false, null, - ], - [[SC::class, "->tmissing"], - false, null, - false, null, - ], - ]; - - function testFunction() { - foreach (self::FUNCTION_TESTS as $args) { - [$func, - $verifix1, $func1, - $verifix2, $func2, - ] = $args; - if ($func === ["", "tsimple"]) { - //echo "breakpoint"; - } - - $workf = $func; - $msg = var_export($func, true)." (strict)"; - self::assertSame($verifix1, func::verifix_function($workf, true), "$msg --> verifix"); - if ($verifix1) { - self::assertSame($func1, $workf, "$msg --> func"); - } - - $workf = $func; - $msg = var_export($func, true)." (lenient)"; - self::assertSame($verifix2, func::verifix_function($workf, false), "$msg --> verifix"); - if ($verifix2) { - self::assertSame($func2, $workf, "$msg --> func"); - } - } - } - - const STATIC_TESTS = [ - # scalaires - [null, - false, null, null, - false, null, null, - ], - [false, - false, null, null, - false, null, null, - ], - ["", - false, null, null, - false, null, null, - ], - ["::", - false, null, null, - false, null, null, - ], - ["->", - false, null, null, - false, null, null, - ], - ["tsimple", - false, null, null, - false, null, null, - ], - ['nur\sery\wip\php\impl\ntsimple', - false, null, null, - false, null, null, - ], - ['tmissing', - false, null, null, - false, null, null, - ], - ["::tstatic", - true, false, [null, "tstatic"], - true, false, [null, "tstatic"], - ], - ["->tmethod", - false, null, null, - false, null, null, - ], - ["::tmissing", - true, false, [null, "tmissing"], - true, false, [null, "tmissing"], - ], - ["->tmissing", - false, null, null, - false, null, null, - ], - ["xxx::tmissing", - false, null, null, - true, true, ["xxx", "tmissing"], - ], - ["xxx->tmissing", - false, null, null, - false, null, null, - ], - [SC::class."::tstatic", - true, true, [SC::class, "tstatic"], - true, true, [SC::class, "tstatic"], - ], - [SC::class."->tmethod", - false, null, null, - false, null, null, - ], - [SC::class."::tmissing", - false, null, null, - true, true, [SC::class, "tmissing"], - ], - [SC::class."->tmissing", - false, null, null, - false, null, null, - ], - # tableaux avec un seul scalaire - [[], - false, null, null, - false, null, null, - ], - [[null], - false, null, null, - false, null, null, - ], - [[false], - false, null, null, - false, null, null, - ], - [[""], - false, null, null, - false, null, null, - ], - [["::"], - false, null, null, - false, null, null, - ], - [["->"], - false, null, null, - false, null, null, - ], - [["tsimple"], - false, null, null, - false, null, null, - ], - [['nur\sery\wip\php\impl\ntsimple'], - false, null, null, - false, null, null, - ], - [["::tstatic"], - true, false, [null, "tstatic"], - true, false, [null, "tstatic"], - ], - [["->tmethod"], - false, null, null, - false, null, null, - ], - [["::tmissing"], - true, false, [null, "tmissing"], - true, false, [null, "tmissing"], - ], - [["->tmissing"], - false, null, null, - false, null, null, - ], - [["xxx::tmissing"], - false, null, null, - true, true, ["xxx", "tmissing"], - ], - [["xxx->tmissing"], - false, null, null, - false, null, null, - ], - [[SC::class."::tstatic"], - true, true, [SC::class, "tstatic"], - true, true, [SC::class, "tstatic"], - ], - [[SC::class."->tmethod"], - false, null, null, - false, null, null, - ], - [[SC::class."::tmissing"], - false, null, null, - true, true, [SC::class, "tmissing"], - ], - [[SC::class."->tmissing"], - false, null, null, - false, null, null, - ], - # tableaux avec deux scalaires - [[null, "tsimple"], - true, false, [null, "tsimple"], - true, false, [null, "tsimple"], - ], - [[null, 'nur\sery\wip\php\impl\ntsimple'], - false, null, null, - false, null, null, - ], - [[null, "tmissing"], - true, false, [null, "tmissing"], - true, false, [null, "tmissing"], - ], - [[null, "::tstatic"], - true, false, [null, "tstatic"], - true, false, [null, "tstatic"], - ], - [[null, "->tmethod"], - false, null, null, - false, null, null, - ], - [[null, "::tmissing"], - true, false, [null, "tmissing"], - true, false, [null, "tmissing"], - ], - [[null, "->tmissing"], - false, null, null, - false, null, null, - ], - [[false, "tsimple"], - false, null, null, - false, null, null, - ], - [[false, 'nur\sery\wip\php\impl\ntsimple'], - false, null, null, - false, null, null, - ], - [[false, "tmissing"], - false, null, null, - false, null, null, - ], - [[false, "::tstatic"], - false, null, null, - false, null, null, - ], - [[false, "->tmethod"], - false, null, null, - false, null, null, - ], - [[false, "::tmissing"], - false, null, null, - false, null, null, - ], - [[false, "->tmissing"], - false, null, null, - false, null, null, - ], - [["", "tsimple"], - false, null, null, - false, null, null, - ], - [["", 'nur\sery\wip\php\impl\ntsimple'], - false, null, null, - false, null, null, - ], - [["", "tmissing"], - false, null, null, - false, null, null, - ], - [["", "::tstatic"], - false, null, null, - false, null, null, - ], - [["", "->tmethod"], - false, null, null, - false, null, null, - ], - [["", "::tmissing"], - false, null, null, - false, null, null, - ], - [["", "->tmissing"], - false, null, null, - false, null, null, - ], - [["xxx", "tmissing"], - false, null, null, - true, true, ["xxx", "tmissing"], - ], - [["xxx", "::tmissing"], - false, null, null, - true, true, ["xxx", "tmissing"], - ], - [["xxx", "->tmissing"], - false, null, null, - false, null, null, - ], - [[SC::class, "tstatic"], - true, true, [SC::class, "tstatic"], - true, true, [SC::class, "tstatic"], - ], - [[SC::class, "::tstatic"], - true, true, [SC::class, "tstatic"], - true, true, [SC::class, "tstatic"], - ], - [[SC::class, "tmethod"], - true, true, [SC::class, "tmethod"], - true, true, [SC::class, "tmethod"], - ], - [[SC::class, "->tmethod"], - false, null, null, - false, null, null, - ], - [[SC::class, "tmissing"], - false, null, null, - true, true, [SC::class, "tmissing"], - ], - [[SC::class, "::tmissing"], - false, null, null, - true, true, [SC::class, "tmissing"], - ], - [[SC::class, "->tmissing"], - false, null, null, - false, null, null, - ], - ]; - - function testStatic() { - foreach (self::STATIC_TESTS as $args) { - [$func, - $verifix1, $bound1, $func1, - $verifix2, $bound2, $func2, - ] = $args; - if ($func === ["", "tsimple"]) { - //echo "breakpoint"; - } - - $workf = $func; - $msg = var_export($func, true)." (strict)"; - self::assertSame($verifix1, func::verifix_static($workf, true, $bound), "$msg --> verifix"); - if ($verifix1) { - self::assertSame($bound1, $bound, "$msg --> bound"); - self::assertSame($func1, $workf, "$msg --> func"); - } - - $workf = $func; - $msg = var_export($func, true)." (lenient)"; - self::assertSame($verifix2, func::verifix_static($workf, false, $bound), "$msg --> verifix"); - if ($verifix2) { - self::assertSame($bound2, $bound, "$msg --> bound"); - self::assertSame($func2, $workf, "$msg --> func"); - } - } - } - - const METHOD_TESTS = [ - # scalaires - [null, - false, null, null, - false, null, null, - ], - [false, - false, null, null, - false, null, null, - ], - ["", - false, null, null, - false, null, null, - ], - ["::", - false, null, null, - false, null, null, - ], - ["->", - false, null, null, - false, null, null, - ], - ["tsimple", - false, null, null, - false, null, null, - ], - ['nur\sery\wip\php\impl\ntsimple', - false, null, null, - false, null, null, - ], - ['tmissing', - false, null, null, - false, null, null, - ], - ["::tstatic", - false, null, null, - false, null, null, - ], - ["->tmethod", - true, false, [null, "tmethod"], - true, false, [null, "tmethod"], - ], - ["::tmissing", - false, null, null, - false, null, null, - ], - ["->tmissing", - true, false, [null, "tmissing"], - true, false, [null, "tmissing"], - ], - ["xxx::tmissing", - false, null, null, - false, null, null, - ], - ["xxx->tmissing", - false, null, null, - true, true, ["xxx", "tmissing"], - ], - [SC::class."::tstatic", - false, null, null, - false, null, null, - ], - [SC::class."->tmethod", - true, true, [SC::class, "tmethod"], - true, true, [SC::class, "tmethod"], - ], - [SC::class."::tmissing", - false, null, null, - false, null, null, - ], - [SC::class."->tmissing", - false, null, null, - true, true, [SC::class, "tmissing"], - ], - # tableaux avec un seul scalaire - [[], - false, null, null, - false, null, null, - ], - [[null], - false, null, null, - false, null, null, - ], - [[false], - false, null, null, - false, null, null, - ], - [[""], - false, null, null, - false, null, null, - ], - [["::"], - false, null, null, - false, null, null, - ], - [["->"], - false, null, null, - false, null, null, - ], - [["tsimple"], - false, null, null, - false, null, null, - ], - [['nur\sery\wip\php\impl\ntsimple'], - false, null, null, - false, null, null, - ], - [["::tstatic"], - false, null, null, - false, null, null, - ], - [["->tmethod"], - true, false, [null, "tmethod"], - true, false, [null, "tmethod"], - ], - [["::tmissing"], - false, null, null, - false, null, null, - ], - [["->tmissing"], - true, false, [null, "tmissing"], - true, false, [null, "tmissing"], - ], - [["xxx::tmissing"], - false, null, null, - false, null, null, - ], - [["xxx->tmissing"], - false, null, null, - true, true, ["xxx", "tmissing"], - ], - [[SC::class."::tstatic"], - false, null, null, - false, null, null, - ], - [[SC::class."->tmethod"], - true, true, [SC::class, "tmethod"], - true, true, [SC::class, "tmethod"], - ], - [[SC::class."::tmissing"], - false, null, null, - false, null, null, - ], - [[SC::class."->tmissing"], - false, null, null, - true, true, [SC::class, "tmissing"], - ], - # tableaux avec deux scalaires - [[null, "tsimple"], - true, false, [null, "tsimple"], - true, false, [null, "tsimple"], - ], - [[null, 'nur\sery\wip\php\impl\ntsimple'], - false, null, null, - false, null, null, - ], - [[null, "tmissing"], - true, false, [null, "tmissing"], - true, false, [null, "tmissing"], - ], - [[null, "::tstatic"], - false, null, null, - false, null, null, - ], - [[null, "->tmethod"], - true, false, [null, "tmethod"], - true, false, [null, "tmethod"], - ], - [[null, "::tmissing"], - false, null, null, - false, null, null, - ], - [[null, "->tmissing"], - true, false, [null, "tmissing"], - true, false, [null, "tmissing"], - ], - [[false, "tsimple"], - false, null, null, - false, null, null, - ], - [[false, 'nur\sery\wip\php\impl\ntsimple'], - false, null, null, - false, null, null, - ], - [[false, "tmissing"], - false, null, null, - false, null, null, - ], - [[false, "::tstatic"], - false, null, null, - false, null, null, - ], - [[false, "->tmethod"], - false, null, null, - false, null, null, - ], - [[false, "::tmissing"], - false, null, null, - false, null, null, - ], - [[false, "->tmissing"], - false, null, null, - false, null, null, - ], - [["", "tsimple"], - false, null, null, - false, null, null, - ], - [["", 'nur\sery\wip\php\impl\ntsimple'], - false, null, null, - false, null, null, - ], - [["", "tmissing"], - false, null, null, - false, null, null, - ], - [["", "::tstatic"], - false, null, null, - false, null, null, - ], - [["", "->tmethod"], - false, null, null, - false, null, null, - ], - [["", "::tmissing"], - false, null, null, - false, null, null, - ], - [["", "->tmissing"], - false, null, null, - false, null, null, - ], - [["xxx", "tmissing"], - false, null, null, - true, true, ["xxx", "tmissing"], - ], - [["xxx", "::tmissing"], - false, null, null, - false, null, null, - ], - [["xxx", "->tmissing"], - false, null, null, - true, true, ["xxx", "tmissing"], - ], - [[SC::class, "tstatic"], - true, true, [SC::class, "tstatic"], - true, true, [SC::class, "tstatic"], - ], - [[SC::class, "::tstatic"], - false, null, null, - false, null, null, - ], - [[SC::class, "tmethod"], - true, true, [SC::class, "tmethod"], - true, true, [SC::class, "tmethod"], - ], - [[SC::class, "->tmethod"], - true, true, [SC::class, "tmethod"], - true, true, [SC::class, "tmethod"], - ], - [[SC::class, "tmissing"], - false, null, null, - true, true, [SC::class, "tmissing"], - ], - [[SC::class, "::tmissing"], - false, null, null, - false, null, null, - ], - [[SC::class, "->tmissing"], - false, null, null, - true, true, [SC::class, "tmissing"], - ], - ]; - - function testMethod() { - foreach (self::METHOD_TESTS as $args) { - [$func, - $verifix1, $bound1, $func1, - $verifix2, $bound2, $func2, - ] = $args; - - $workf = $func; - $msg = var_export($func, true)." (strict)"; - self::assertSame($verifix1, func::verifix_method($workf, true, $bound), "$msg --> verifix"); - if ($verifix1) { - self::assertSame($bound1, $bound, "$msg --> bound"); - self::assertSame($func1, $workf, "$msg --> func"); - } - - $workf = $func; - $msg = var_export($func, true)." (lenient)"; - self::assertSame($verifix2, func::verifix_method($workf, false, $bound), "$msg --> verifix"); - if ($verifix2) { - self::assertSame($bound2, $bound, "$msg --> bound"); - self::assertSame($func2, $workf, "$msg --> func"); - } - } - } - - function testInvokeFunction() { - # m1 - self::assertSame([null], func::call("tm1")); - self::assertSame([null], func::call("tm1", null)); - self::assertSame([null], func::call("tm1", null, null)); - self::assertSame([null], func::call("tm1", null, null, null)); - self::assertSame([null], func::call("tm1", null, null, null, null)); - self::assertSame([1], func::call("tm1", 1)); - self::assertSame([1], func::call("tm1", 1, 2)); - self::assertSame([1], func::call("tm1", 1, 2, 3)); - self::assertSame([1], func::call("tm1", 1, 2, 3, 4)); - - # o1 - self::assertSame([9], func::call("to1")); - self::assertSame([null], func::call("to1", null)); - self::assertSame([null], func::call("to1", null, null)); - self::assertSame([null], func::call("to1", null, null, null)); - self::assertSame([null], func::call("to1", null, null, null, null)); - self::assertSame([1], func::call("to1", 1)); - self::assertSame([1], func::call("to1", 1, 2)); - self::assertSame([1], func::call("to1", 1, 2, 3)); - self::assertSame([1], func::call("to1", 1, 2, 3, 4)); - - # v - self::assertSame([], func::call("tv")); - self::assertSame([null], func::call("tv", null)); - self::assertSame([null, null], func::call("tv", null, null)); - self::assertSame([null, null, null], func::call("tv", null, null, null)); - self::assertSame([null, null, null, null], func::call("tv", null, null, null, null)); - self::assertSame([1], func::call("tv", 1)); - self::assertSame([1, 2], func::call("tv", 1, 2)); - self::assertSame([1, 2, 3], func::call("tv", 1, 2, 3)); - self::assertSame([1, 2, 3, 4], func::call("tv", 1, 2, 3, 4)); - - # m1o1 - self::assertSame([null, 9], func::call("tm1o1")); - self::assertSame([null, 9], func::call("tm1o1", null)); - self::assertSame([null, null], func::call("tm1o1", null, null)); - self::assertSame([null, null], func::call("tm1o1", null, null, null)); - self::assertSame([null, null], func::call("tm1o1", null, null, null, null)); - self::assertSame([1, 9], func::call("tm1o1", 1)); - self::assertSame([1, 2], func::call("tm1o1", 1, 2)); - self::assertSame([1, 2], func::call("tm1o1", 1, 2, 3)); - self::assertSame([1, 2], func::call("tm1o1", 1, 2, 3, 4)); - - # m1v - self::assertSame([null], func::call("tm1v")); - self::assertSame([null], func::call("tm1v", null)); - self::assertSame([null, null], func::call("tm1v", null, null)); - self::assertSame([null, null, null], func::call("tm1v", null, null, null)); - self::assertSame([null, null, null, null], func::call("tm1v", null, null, null, null)); - self::assertSame([1], func::call("tm1v", 1)); - self::assertSame([1, 2], func::call("tm1v", 1, 2)); - self::assertSame([1, 2, 3], func::call("tm1v", 1, 2, 3)); - self::assertSame([1, 2, 3, 4], func::call("tm1v", 1, 2, 3, 4)); - - # m1o1v - self::assertSame([null, 9], func::call("tm1o1v")); - self::assertSame([null, 9], func::call("tm1o1v", null)); - self::assertSame([null, null], func::call("tm1o1v", null, null)); - self::assertSame([null, null, null], func::call("tm1o1v", null, null, null)); - self::assertSame([null, null, null, null], func::call("tm1o1v", null, null, null, null)); - self::assertSame([1, 9], func::call("tm1o1v", 1)); - self::assertSame([1, 2], func::call("tm1o1v", 1, 2)); - self::assertSame([1, 2, 3], func::call("tm1o1v", 1, 2, 3)); - self::assertSame([1, 2, 3, 4], func::call("tm1o1v", 1, 2, 3, 4)); - - # o1v - self::assertSame([9], func::call("to1v")); - self::assertSame([null], func::call("to1v", null)); - self::assertSame([null, null], func::call("to1v", null, null)); - self::assertSame([null, null, null], func::call("to1v", null, null, null)); - self::assertSame([null, null, null, null], func::call("to1v", null, null, null, null)); - self::assertSame([1], func::call("to1v", 1)); - self::assertSame([1, 2], func::call("to1v", 1, 2)); - self::assertSame([1, 2, 3], func::call("to1v", 1, 2, 3)); - self::assertSame([1, 2, 3, 4], func::call("to1v", 1, 2, 3, 4)); - } - - function testInvokeClass() { - $func = func::with(SC::class); - self::assertInstanceOf(SC::class, $func->invoke()); - self::assertInstanceOf(SC::class, $func->invoke([])); - self::assertInstanceOf(SC::class, $func->invoke([1])); - self::assertInstanceOf(SC::class, $func->invoke([1, 2])); - self::assertInstanceOf(SC::class, $func->invoke([1, 2, 3])); - - $func = func::with(C0::class); - self::assertInstanceOf(C0::class, $func->invoke()); - self::assertInstanceOf(C0::class, $func->invoke([])); - self::assertInstanceOf(C0::class, $func->invoke([1])); - self::assertInstanceOf(C0::class, $func->invoke([1, 2])); - self::assertInstanceOf(C0::class, $func->invoke([1, 2, 3])); - - $func = func::with(C1::class); - /** @var C1 $i1 */ - $i1 = $func->invoke(); - self::assertInstanceOf(C1::class, $i1); self::assertSame(null, $i1->first); - $i1 = $func->invoke([]); - self::assertInstanceOf(C1::class, $i1); self::assertSame(null, $i1->first); - $i1 = $func->invoke([1]); - self::assertInstanceOf(C1::class, $i1); self::assertSame(1, $i1->first); - $i1 = $func->invoke([1, 2]); - self::assertInstanceOf(C1::class, $i1); self::assertSame(1, $i1->first); - } - - function testInvokeStatic() { - $func = func::with([SC::class, "tstatic"]); - self::assertSame(10, $func->invoke()); - - $func = func::with([SC::class, "::tstatic"]); - self::assertSame(10, $func->invoke()); - - $func = func::with([null, "tstatic"]); - self::assertException(ValueException::class, function() use ($func) { - $func->invoke(); - }); - - $func = func::with([null, "::tstatic"]); - self::assertException(ValueException::class, function() use ($func) { - $func->invoke(); - }); - - $func = func::with([null, "tstatic"]); - self::assertSame(10, $func->bind(SC::class)->invoke()); - - $func = func::with([null, "tstatic"]); - self::assertSame(10, $func->bind(new SC())->invoke()); - - $func = func::with([null, "::tstatic"]); - self::assertSame(10, $func->bind(SC::class)->invoke()); - - $func = func::with([null, "::tstatic"]); - self::assertSame(10, $func->bind(new SC())->invoke()); - - $func = func::with([SC::class, "tmethod"]); - self::assertException(ReflectionException::class, function() use ($func) { - $func->invoke(); - }); - - $func = func::with([SC::class, "::tmethod"]); - self::assertException(ReflectionException::class, function() use ($func) { - $func->invoke(); - }); - - $func = func::with([null, "tmethod"]); - self::assertException(ValueException::class, function() use ($func) { - $func->invoke(); - }); - - $func = func::with([null, "::tmethod"]); - self::assertException(ValueException::class, function() use ($func) { - $func->invoke(); - }); - - $func = func::with([null, "tmethod"]); - self::assertException(ReflectionException::class, function() use ($func) { - $func->bind(new SC())->invoke(); - }); - - $func = func::with([null, "::tmethod"]); - self::assertException(ReflectionException::class, function() use ($func) { - $func->bind(new SC())->invoke(); - }); - } - - function testInvokeMethod() { - $func = func::with([SC::class, "tstatic"]); - self::assertSame(10, $func->invoke()); - - $func = func::with([SC::class, "->tstatic"]); - self::assertSame(10, $func->invoke()); - - $func = func::with([null, "tstatic"]); - self::assertException(ValueException::class, function() use ($func) { - $func->invoke(); - }); - - $func = func::with([null, "->tstatic"]); - self::assertException(ValueException::class, function() use ($func) { - $func->invoke(); - }); - - $func = func::with([null, "tstatic"]); - self::assertSame(10, $func->bind(new SC())->invoke()); - - $func = func::with([null, "->tstatic"]); - self::assertSame(10, $func->bind(new SC())->invoke()); - - $func = func::with([SC::class, "tmethod"]); - self::assertException(ReflectionException::class, function() use ($func) { - $func->invoke(); - }); - - $func = func::with([SC::class, "->tmethod"]); - self::assertException(ReflectionException::class, function() use ($func) { - $func->invoke(); - }); - - $func = func::with([null, "tmethod"]); - self::assertException(ValueException::class, function() use ($func) { - $func->invoke(); - }); - - $func = func::with([null, "->tmethod"]); - self::assertException(ValueException::class, function() use ($func) { - $func->invoke(); - }); - - $func = func::with([null, "tmethod"]); - self::assertException(ReflectionException::class, function() use ($func) { - $func->bind(new SC())->invoke(); - }); - - $func = func::with([null, "->tmethod"]); - self::assertSame(11, $func->bind(new SC())->invoke()); - } - } -} diff --git a/tests/wip/php/impl/C0.php b/tests/wip/php/impl/C0.php deleted file mode 100644 index f9c7619..0000000 --- a/tests/wip/php/impl/C0.php +++ /dev/null @@ -1,15 +0,0 @@ -first = $first; - } - - public $first; - - static function tstatic(): int { - return 10; - } - - function tmethod(): int { - return 11; - } -} diff --git a/tests/wip/php/impl/SC.php b/tests/wip/php/impl/SC.php deleted file mode 100644 index 2c8ae3c..0000000 --- a/tests/wip/php/impl/SC.php +++ /dev/null @@ -1,12 +0,0 @@ -invalidDef("dynamic_command"); @@ -1399,21 +1399,21 @@ class ArgsParser { } else { if ($action === null) { # NOP - } elseif (func::is_method($action)) { + } elseif (nur_func::is_method($action)) { # méthode $func = $action; $func_args = [$value, $name, $arg, $this->dest, $def]; - func::fix_method($func, $this->dest); - func::fix_args($func, $func_args); - func::call($func, ...$func_args); + nur_func::fix_method($func, $this->dest); + nur_func::fix_args($func, $func_args); + nur_func::call($func, ...$func_args); return; } elseif (!is_string($action) || substr($action, 0, 2) != "--") { # fonction statique $func = $action; $func_args = [$value, $name, $arg, $this->dest, $def]; - func::fix_static($func, $this->dest); - func::fix_args($func, $func_args); - func::call($func, ...$func_args); + nur_func::fix_static($func, $this->dest); + nur_func::fix_args($func, $func_args); + nur_func::call($func, ...$func_args); return; } diff --git a/wip/cli/DynamicCommand.php b/wip/cli/DynamicCommand.php index cbd1959..38be321 100644 --- a/wip/cli/DynamicCommand.php +++ b/wip/cli/DynamicCommand.php @@ -2,7 +2,7 @@ namespace nur\sery\wip\cli; use nur\A; -use nur\sery\php\func; +use nur\sery\php\nur_func; /** * Class DynamicCommand: implémentation par défaut de {@link IDynamicCommand} @@ -75,7 +75,7 @@ class DynamicCommand implements IDynamicCommand { } # tester les commandes complètement dynamiques foreach ($this->dcommands as $func) { - $cdef = func::call($func, $command); + $cdef = nur_func::call($func, $command); if ($cdef !== null) return [$cdef]; } return null; diff --git a/wip/cli/DynamicCommandMethod.php b/wip/cli/DynamicCommandMethod.php index f59c879..2375fd2 100644 --- a/wip/cli/DynamicCommandMethod.php +++ b/wip/cli/DynamicCommandMethod.php @@ -1,7 +1,7 @@ func; $func_args = [$command]; - func::check_func($func, $this->dest, $func_args); - return func::call($func, ...$func_args); + nur_func::check_func($func, $this->dest, $func_args); + return nur_func::call($func, ...$func_args); } } diff --git a/wip/php/func.php b/wip/php/func.php deleted file mode 100644 index c98f76d..0000000 --- a/wip/php/func.php +++ /dev/null @@ -1,630 +0,0 @@ -"; - } - - private static function _is_nfunction(?string $f): bool { - return strpos($f, "\\") !== false; - } - - private static function _parse_static(?string &$m): bool { - $pos = strpos($m, "::"); - if ($pos === false) return false; - $m = substr($m, $pos + 2); - return true; - } - - private static function _parse_method(?string &$m): bool { - $pos = strpos($m, "->"); - if ($pos === false) return false; - $m = substr($m, $pos + 2); - return true; - } - - ############################################################################# - # Fonctions - - /** - * vérifier que $func est une fonction simple et la normaliser le cas échéant. - * retourner true si c'est une fonction simple, false sinon - * - * les formes suivantes sont supportées: - * - "function" si une classe du même nom n'existe pas déjà - * - [false, "function", ...$args] c'est la forme normalisée - * - * @param bool $strict vérifier l'inexistence de la classe et l'existence de - * la fonction (ne pas uniquement faire une vérification syntaxique) - */ - static function verifix_function(&$func, bool $strict=true, ?string &$reason=null): bool { - if ($strict) { - $msg = var_export($func, true); - $reason = null; - } - if ($func instanceof ReflectionFunction) return true; - if (is_string($func)) { - $c = false; - $f = $func; - } elseif (is_array($func)) { - if (!array_key_exists(0, $func)) return false; - $c = $func[0]; - if (!array_key_exists(1, $func)) return false; - $f = $func[1]; - } else { - return false; - } - if ($c !== false) return false; - if (!is_string($f)) return false; - if (self::_is_invalid($f)) return false; - if (self::_parse_static($f)) return false; - if (self::_parse_method($f)) return false; - if ($strict) { - $reason = null; - if (class_exists($f)) { - $reason = "$msg: is a class"; - return false; - } - if (!function_exists($f)) { - $reason = "$msg: function not found"; - return false; - } - } - $func = [false, $f]; - return true; - } - - /** - * vérifier que $func est une fonction simple avec les règles de - * {@link self::verifix_function()} - */ - static function is_function($func, bool $strict=true, ?string &$reason=null): bool { - return self::verifix_function($func, $strict, $reason); - } - - ############################################################################# - # Classes - - /** - * vérifier que $func est une classe et la normaliser le cas échéant. - * retourner true si c'est une classe, false sinon - * - * les formes suivantes sont supportées: - * - "class" - * - ["class", false, ...$args] c'est la forme normalisée - * - * @param bool $strict vérifier l'existence de la classe (ne pas uniquement - * faire une vérification syntaxique) - */ - static function verifix_class(&$func, bool $strict=true, ?string &$reason=null): bool { - if ($strict) { - $msg = var_export($func, true); - $reason = null; - } - if ($func instanceof ReflectionClass) return true; - if (is_string($func)) { - $c = $func; - $f = false; - } elseif (is_array($func)) { - if (!array_key_exists(0, $func)) return false; - $c = $func[0]; - if (!array_key_exists(1, $func)) return false; - $f = $func[1]; - } else { - return false; - } - if (!is_string($c)) return false; - if (self::_is_invalid($c)) return false; - if (self::_parse_static($c)) return false; - if (self::_parse_method($c)) return false; - if ($f !== false) return false; - if ($strict) { - if (!class_exists($c)) { - $reason = "$msg: class not found"; - return false; - } - } - $func = [$c, false]; - return true; - } - - /** - * vérifier que $func est une classe avec les règles de - * {@link self::verifix_class()} - */ - static function is_class($func, bool $strict=true, ?string &$reason=null): bool { - return self::verifix_class($func, $strict, $reason); - } - - ############################################################################# - # Méthodes statiques - - private static function _parse_class_s(?string $cs, ?string &$c, ?string &$s): bool { - if (self::_is_invalid($cs) || self::_parse_method($cs)) return false; - $pos = strpos($cs, "::"); - if ($pos === false) return false; - if ($pos === 0) return false; - $tmpc = substr($cs, 0, $pos); - $cs = substr($cs, $pos + 2); - if (self::_is_nfunction($cs)) return false; - [$c, $s] = [$tmpc, cv::vn($cs)]; - return true; - } - - private static function _parse_c_static(?string $cs, ?string &$c, ?string &$s, ?bool &$bound): bool { - if (self::_is_invalid($cs) || self::_parse_method($cs)) return false; - $pos = strpos($cs, "::"); - if ($pos === false) return false; - if ($pos == strlen($cs) - 2) return false; - if ($pos > 0) { - $tmpc = substr($cs, 0, $pos); - $bound = true; - } else { - $tmpc = null; - $bound = false; - } - $cs = substr($cs, $pos + 2); - if (self::_is_nfunction($cs)) return false; - [$c, $s] = [$tmpc, cv::vn($cs)]; - return true; - } - - /** - * vérifier que $func est une méthode statique, et la normaliser le cas - * échéant. retourner true si c'est une méthode statique, false sinon - * - * les formes suivantes sont supportées (XXX étant null ou n'importe quelle - * valeur scalaire de n'importe quel type sauf false) - * - "XXX::function" - * - ["XXX::function", ...$args] - * - [XXX, "::function", ...$args] - * - [XXX, "function", ...$args] c'est la forme normalisée - * - * Si XXX est une classe, la méthode statique est liée. sinon, elle doit être - * liée à une classe avant d'être utilisée - * - * @param bool $strict vérifier l'existence de la classe et de la méthode si - * la méthode est liée (ne pas uniquement faire une vérification syntaxique) - */ - static function verifix_static(&$func, bool $strict=true, ?bool &$bound=null, ?string &$reason=null): bool { - if ($strict) { - $msg = var_export($func, true); - $reason = null; - } - if ($func instanceof ReflectionMethod) { - $bound = false; - return true; - } - if (is_string($func)) { - if (!self::_parse_c_static($func, $c, $f, $bound)) return false; - $cf = [$c, $f]; - } elseif (is_array($func)) { - $cf = $func; - if (!array_key_exists(0, $cf)) return false; - $c = $cf[0]; - if ($c === false) return false; - if (is_object($c)) $c = get_class($c); - if (is_string($c)) { - if (self::_is_invalid($c)) return false; - if (self::_parse_class_s($c, $c, $f)) { - $cf[0] = $c; - if ($f !== null) { - # ["class::method"] --> ["class", "method"] - array_splice($cf, 1, 0, [$f]); - } - $bound = true; - } elseif (self::_parse_c_static($c, $c, $f, $bound)) { - # ["::method"] --> [null, "method"] - array_splice($cf, 0, 0, [null]); - $cf[1] = $f; - } else { - $cf[0] = $c; - $bound = is_string($c); - } - } else { - $cf[0] = null; - $bound = false; - } - # - if (!array_key_exists(1, $cf)) return false; - $f = $cf[1]; - if (!is_string($f)) return false; - if (self::_parse_c_static($f, $rc, $f, $rbound)) { - if ($rc !== null && $c === null) { - $c = $rc; - $bound = $rbound; - } - } else { - if (self::_is_invalid($f)) return false; - if (self::_is_nfunction($f)) return false; - if (self::_parse_method($f)) return false; - self::_parse_static($f); - } - $cf[1] = $f; - } else { - return false; - } - if ($strict) { - $reason = null; - if ($bound) { - if (!class_exists($c)) { - $reason = "$msg: class not found"; - return false; - } - if (!method_exists($c, $f)) { - $reason = "$msg: method not found"; - return false; - } - } else { - $reason = "$msg: not bound"; - } - } - $func = $cf; - return true; - } - - /** - * vérifier que $func est une méthode statique avec les règles de - * {@link self::verifix_static()} - */ - static function is_static($func, bool $strict=true, ?bool &$bound=null, ?string &$reason=null): bool { - return self::verifix_static($func, $strict, $bound, $reason); - } - - ############################################################################# - # Méthodes non statiques - - private static function _parse_class_m(?string $cm, ?string &$c, ?string &$m): bool { - if (self::_is_invalid($cm) || self::_parse_static($cm)) return false; - $pos = strpos($cm, "->"); - if ($pos === false) return false; - if ($pos === 0) return false; - $tmpc = substr($cm, 0, $pos); - $cm = substr($cm, $pos + 2); - if (self::_is_nfunction($cm)) return false; - [$c, $m] = [$tmpc, cv::vn($cm)]; - return true; - } - - private static function _parse_c_method(?string $cm, ?string &$c, ?string &$m, ?bool &$bound): bool { - if (self::_is_invalid($cm) || self::_parse_static($cm)) return false; - $pos = strpos($cm, "->"); - if ($pos === false) return false; - if ($pos == strlen($cm) - 2) return false; - if ($pos > 0) { - $tmpc = substr($cm, 0, $pos); - $bound = true; - } else { - $tmpc = null; - $bound = false; - } - $cm = substr($cm, $pos + 2); - if (self::_is_nfunction($cm)) return false; - [$c, $m] = [$tmpc, cv::vn($cm)]; - return true; - } - - /** - * vérifier que $func est une méthode non statique, et la normaliser le cas - * échéant. retourner true si c'est une méthode non statique, false sinon - * - * les formes suivantes sont supportées (XXX étant null ou n'importe quelle - * valeur scalaire de n'importe quel type sauf false) - * - "XXX->function" - * - ["XXX->function", ...$args] - * - [XXX, "->function", ...$args] - * - [XXX, "function", ...$args] c'est la forme normalisée - * - * Si XXX est une classe, la méthode est liée. sinon, elle doit être liée à un - * objet avant d'être utilisée - * - * @param bool $strict vérifier l'existence de la classe et de la méthode si - * la méthode est liée (ne pas uniquement faire une vérification syntaxique) - */ - static function verifix_method(&$func, bool $strict=true, ?bool &$bound=null, ?string &$reason=null): bool { - if ($strict) { - $msg = var_export($func, true); - $reason = null; - } - if ($func instanceof ReflectionMethod) { - $bound = false; - return true; - } - if (is_string($func)) { - if (!self::_parse_c_method($func, $c, $f, $bound)) return false; - $cf = [$c, $f]; - } elseif (is_array($func)) { - $cf = $func; - if (!array_key_exists(0, $cf)) return false; - $c = $cf[0]; - if ($c === false) return false; - if (is_object($c)) { - $bound = true; - } elseif (is_string($c)) { - if (self::_is_invalid($c)) return false; - if (self::_parse_class_m($c, $c, $f)) { - $cf[0] = $c; - if ($f !== null) { - # ["class->method"] --> ["class", "method"] - array_splice($cf, 1, 0, [$f]); - } - $bound = true; - } elseif (self::_parse_c_method($c, $c, $f, $bound)) { - # ["->method"] --> [null, "method"] - array_splice($cf, 0, 0, [null]); - $cf[1] = $f; - } else { - $cf[0] = $c; - $bound = is_string($c); - } - } else { - $cf[0] = null; - $bound = false; - } - # - if (!array_key_exists(1, $cf)) return false; - $f = $cf[1]; - if (!is_string($f)) return false; - if (self::_parse_c_method($f, $rc, $f, $rbound)) { - if ($rc !== null && $c === null) { - $c = $rc; - $bound = $rbound; - } - } else { - if (self::_is_invalid($f)) return false; - if (self::_is_nfunction($f)) return false; - if (self::_parse_static($f)) return false; - self::_parse_method($f); - } - $cf[1] = $f; - } else { - return false; - } - if ($strict) { - $reason = null; - if ($bound) { - if (!is_object($c) && !class_exists($c)) { - $reason = "$msg: class not found"; - return false; - } - if (!method_exists($c, $f)) { - $reason = "$msg: method not found"; - return false; - } - } else { - $reason = "$msg: not bound"; - } - } - $func = $cf; - return true; - } - - /** - * vérifier que $func est une méthode non statique avec les règles de - * {@link self::verifix_method()} - */ - static function is_method($func, bool $strict=true, ?bool &$bound=null, ?string &$reason=null): bool { - return self::verifix_method($func, $strict, $bound, $reason); - } - - ############################################################################# - # func - - const TYPE_CLOSURE = 0, TYPE_SIMPLE = 1, TYPE_CLASS = 2, TYPE_STATIC = 3, TYPE_METHOD = 4; - - protected static function not_a_callable($func, ?string $reason) { - if ($reason === null) { - $msg = var_export($func, true); - $reason = "$msg: not a callable"; - } - return new ValueException($reason); - } - - static function with($func, bool $strict=true): self { - if (!is_array($func)) { - if ($func instanceof Closure) { - return new self(self::TYPE_CLOSURE, $func); - } elseif ($func instanceof ReflectionFunction) { - return new self(self::TYPE_SIMPLE, $func); - } elseif ($func instanceof ReflectionClass) { - return new self(self::TYPE_CLASS, $func); - } elseif ($func instanceof ReflectionMethod) { - return new self(self::TYPE_METHOD, $func, false); - } - } - if (self::verifix_function($func, $strict, $reason)) { - return new self(self::TYPE_SIMPLE, $func, false, $reason); - } elseif (self::verifix_class($func, $strict, $reason)) { - return new self(self::TYPE_CLASS, $func, false, $reason); - } else { - $valid = true; - if (is_array($func) && array_key_exists(0, $func) && is_object($func[0])) { - if (self::verifix_method($func, $strict, $bound, $reason)) { - $type = self::TYPE_METHOD; - } elseif (self::verifix_static($func, $strict, $bound, $reason)) { - $type = self::TYPE_STATIC; - } else { - $valid = false; - } - } else { - if (self::verifix_static($func, $strict, $bound, $reason)) { - $type = self::TYPE_STATIC; - } elseif (self::verifix_method($func, $strict, $bound, $reason)) { - $type = self::TYPE_METHOD; - } else { - $valid = false; - } - } - if ($valid) return new self($type, $func, $bound, $reason); - } - throw self::not_a_callable($func, $reason); - } - - static function call($func, ...$args) { - return self::with($func)->invoke($args); - } - - ############################################################################# - - protected function __construct(int $type, $func, bool $bound=false, ?string $reason=null) { - $object = null; - $prefixArgs = []; - if (!is_array($func)) { - $reflection = $func; - $func = null; - } else { - if (count($func) > 2) { - $prefixArgs = array_slice($func, 2); - $func = array_slice($func, 0, 2); - } - [$c, $f] = $func; - switch ($type) { - case self::TYPE_SIMPLE: - $reflection = new ReflectionFunction($f); - break; - case self::TYPE_CLASS: - $reflection = new ReflectionClass($c); - break; - case self::TYPE_STATIC: - case self::TYPE_METHOD: - if ($c === null) { - $reflection = null; - } else { - $reflection = new ReflectionMethod($c, $f); - if (is_object($c)) $object = $c; - } - break; - default: - throw StateException::unexpected_state(); - } - } - - $this->type = $type; - $this->func = $func; - $this->bound = $bound; - $this->reason = $reason; - $this->object = $object; - $this->prefixArgs = $prefixArgs; - $this->updateReflection($reflection); - } - - protected int $type; - - protected ?array $func; - - protected bool $bound; - - protected ?string $reason; - - protected ?object $object; - - protected array $prefixArgs; - - /** @var Closure|ReflectionFunction|ReflectionMethod|ReflectionClass */ - protected $reflection; - - protected bool $variadic; - - protected int $minArgs; - - protected int $maxArgs; - - protected function updateReflection($reflection): void { - $variadic = false; - $minArgs = $maxArgs = 0; - if ($reflection instanceof Closure) { - } elseif ($reflection instanceof ReflectionClass) { - $constructor = $reflection->getConstructor(); - if ($constructor === null) { - $variadic = false; - $minArgs = $maxArgs = 0; - } else { - $variadic = $constructor->isVariadic(); - $minArgs = $constructor->getNumberOfRequiredParameters(); - $maxArgs = $constructor->getNumberOfParameters(); - } - } elseif ($reflection !== null) { - $variadic = $reflection->isVariadic(); - $minArgs = $reflection->getNumberOfRequiredParameters(); - $maxArgs = $reflection->getNumberOfParameters(); - } - $this->reflection = $reflection; - $this->variadic = $variadic; - $this->minArgs = $minArgs; - $this->maxArgs = $maxArgs; - } - - function bind($object, bool $rebind=false): self { - if ($rebind || !$this->bound) { - switch ($this->type) { - case self::TYPE_STATIC: - if (is_object($object)) $object = get_class($object); - # pas de break, continuer à la section suivante - case self::TYPE_METHOD: - if ($this->reflection === null) { - $this->func[0] = $object; - $this->updateReflection(new ReflectionMethod(...$this->func)); - } - break; - } - if (is_object($object)) { - $this->object = $object; - $this->bound = true; - } - } - return $this; - } - - function invoke(?array $args=null) { - $args = array_merge($this->prefixArgs, $args ?? []); - if (!$this->variadic) $args = array_slice($args, 0, $this->maxArgs); - $minArgs = $this->minArgs; - while (count($args) < $minArgs) $args[] = null; - - switch ($this->type) { - case self::TYPE_CLOSURE: - /** @var Closure $closure */ - $closure = $this->reflection; - return $closure(...$args); - case self::TYPE_SIMPLE: - /** @var ReflectionFunction $function */ - $function = $this->reflection; - return $function->invoke(...$args); - case self::TYPE_STATIC: - case self::TYPE_METHOD: - /** @var ReflectionMethod $method */ - $method = $this->reflection; - if ($method === null) throw self::not_a_callable($this->func, $this->reason); - return $method->invoke($this->object, ...$args); - case self::TYPE_CLASS: - /** @var ReflectionClass $class */ - $class = $this->reflection; - return $class->newInstance(...$args); - default: - throw StateException::unexpected_state(); - } - } -} diff --git a/wip/schema/types/tcallable.php b/wip/schema/types/tcallable.php index 13e405b..a7280c2 100644 --- a/wip/schema/types/tcallable.php +++ b/wip/schema/types/tcallable.php @@ -1,7 +1,7 @@