diff --git a/tests/wip/php/funcTest.php b/tests/wip/php/funcTest.php index f8c3dcc..4b8ade1 100644 --- a/tests/wip/php/funcTest.php +++ b/tests/wip/php/funcTest.php @@ -17,6 +17,7 @@ namespace nur\sery\wip\php\impl { namespace nur\sery\wip\php { use nulib\tests\TestCase; + use nur\sery\ValueException; use nur\sery\wip\php\impl\C0; use nur\sery\wip\php\impl\C1; use nur\sery\wip\php\impl\SC; @@ -1002,7 +1003,7 @@ namespace nur\sery\wip\php { 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()); @@ -1010,14 +1011,14 @@ namespace nur\sery\wip\php { 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(); @@ -1034,36 +1035,112 @@ namespace nur\sery\wip\php { $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([null, "tstatic"]); + $func = func::with([SC::class, "::tmethod"]); self::assertException(ReflectionException::class, function() use ($func) { $func->invoke(); }); $func = func::with([null, "tmethod"]); - self::assertException(ReflectionException::class, function() use ($func) { + 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([new SC(), "tstatic"]); + $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([new SC(), "tmethod"]); - self::assertSame(11, $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/wip/php/func.php b/wip/php/func.php index 08b3f97..c98f76d 100644 --- a/wip/php/func.php +++ b/wip/php/func.php @@ -433,35 +433,54 @@ class func { ############################################################################# # 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 ($func instanceof Closure) { - return new self($func, false, null); - } elseif (self::verifix_function($func, $strict, $reason)) { - return new self($func, false, $reason); + 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($func, false, $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($func, $bound, $reason); + if ($valid) return new self($type, $func, $bound, $reason); } - if ($reason === null) { - $msg = var_export($func, true); - $reason = "$msg: not a callable"; - } - throw new ValueException($reason); + throw self::not_a_callable($func, $reason); } static function call($func, ...$args) { @@ -470,54 +489,74 @@ class func { ############################################################################# - const TYPE_CLOSURE = 0, TYPE_SIMPLE = 1, TYPE_CLASS = 2, TYPE_STATIC = 3, TYPE_METHOD = 4; - - protected function __construct($func, bool $bound=false, ?string $reason=null) { + protected function __construct(int $type, $func, bool $bound=false, ?string $reason=null) { $object = null; $prefixArgs = []; - if ($func instanceof Closure) { - $type = self::TYPE_CLOSURE; - $object = $func; - $reflection = new ReflectionFunction($func); - } elseif ($func instanceof ReflectionFunction) { - $type = self::TYPE_SIMPLE; + if (!is_array($func)) { $reflection = $func; - } elseif ($func instanceof ReflectionClass) { - $type = self::TYPE_CLASS; - $reflection = $func; - } elseif ($func instanceof ReflectionMethod) { - $type = self::TYPE_STATIC; - $reflection = $func; - } elseif (is_array($func)) { + $func = null; + } else { if (count($func) > 2) { $prefixArgs = array_slice($func, 2); $func = array_slice($func, 0, 2); } [$c, $f] = $func; - if ($c === false) { - # fonction simple - $type = self::TYPE_SIMPLE; + switch ($type) { + case self::TYPE_SIMPLE: $reflection = new ReflectionFunction($f); - } elseif ($f === false) { - # classe - $type = self::TYPE_CLASS; + break; + case self::TYPE_CLASS: $reflection = new ReflectionClass($c); - } elseif ($c !== null) { - # methode - $reflection = new ReflectionMethod($c, $f); - if (is_object($c)) { - $type = self::TYPE_METHOD; - $object = $c; + break; + case self::TYPE_STATIC: + case self::TYPE_METHOD: + if ($c === null) { + $reflection = null; } else { - $type = self::TYPE_STATIC; + $reflection = new ReflectionMethod($c, $f); + if (is_object($c)) $object = $c; } - } else { - throw new StateException("invalid func"); + break; + default: + throw StateException::unexpected_state(); } - } else { - throw new StateException("invalid func"); } - if ($reflection instanceof ReflectionClass) { + + $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; @@ -527,42 +566,36 @@ class func { $minArgs = $constructor->getNumberOfRequiredParameters(); $maxArgs = $constructor->getNumberOfParameters(); } - } else { + } elseif ($reflection !== null) { $variadic = $reflection->isVariadic(); $minArgs = $reflection->getNumberOfRequiredParameters(); $maxArgs = $reflection->getNumberOfParameters(); } - - $this->type = $type; $this->reflection = $reflection; $this->variadic = $variadic; $this->minArgs = $minArgs; $this->maxArgs = $maxArgs; - $this->object = $object; - $this->prefixArgs = $prefixArgs; } - protected int $type; - - /** @var ReflectionFunction|ReflectionMethod|ReflectionClass */ - protected $reflection; - - protected bool $variadic; - - protected int $minArgs; - - protected int $maxArgs; - - protected ?object $object; - - protected array $prefixArgs; - - function bind($objectOrClass, bool $rebind=false): self { - - } - - function bindClass($objectOrClass, bool $rebind=false): self { - + 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) { @@ -574,7 +607,7 @@ class func { switch ($this->type) { case self::TYPE_CLOSURE: /** @var Closure $closure */ - $closure = $this->object; + $closure = $this->reflection; return $closure(...$args); case self::TYPE_SIMPLE: /** @var ReflectionFunction $function */ @@ -584,11 +617,14 @@ class func { 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(); } } }