modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2024-09-18 17:23:15 +04:00
parent f3c07d7665
commit 3ce2f8e134
2 changed files with 193 additions and 80 deletions

View File

@ -17,6 +17,7 @@ namespace nur\sery\wip\php\impl {
namespace nur\sery\wip\php { namespace nur\sery\wip\php {
use nulib\tests\TestCase; use nulib\tests\TestCase;
use nur\sery\ValueException;
use nur\sery\wip\php\impl\C0; use nur\sery\wip\php\impl\C0;
use nur\sery\wip\php\impl\C1; use nur\sery\wip\php\impl\C1;
use nur\sery\wip\php\impl\SC; use nur\sery\wip\php\impl\SC;
@ -1034,36 +1035,112 @@ namespace nur\sery\wip\php {
$func = func::with([SC::class, "tstatic"]); $func = func::with([SC::class, "tstatic"]);
self::assertSame(10, $func->invoke()); 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"]); $func = func::with([SC::class, "tmethod"]);
self::assertException(ReflectionException::class, function() use ($func) { self::assertException(ReflectionException::class, function() use ($func) {
$func->invoke(); $func->invoke();
}); });
$func = func::with([null, "tstatic"]); $func = func::with([SC::class, "::tmethod"]);
self::assertException(ReflectionException::class, function() use ($func) { self::assertException(ReflectionException::class, function() use ($func) {
$func->invoke(); $func->invoke();
}); });
$func = func::with([null, "tmethod"]); $func = func::with([null, "tmethod"]);
self::assertException(ReflectionException::class, function() use ($func) { self::assertException(ValueException::class, function() use ($func) {
$func->invoke(); $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() { function testInvokeMethod() {
$func = func::with([SC::class, "tstatic"]); $func = func::with([SC::class, "tstatic"]);
self::assertSame(10, $func->invoke()); self::assertSame(10, $func->invoke());
$func = func::with([new SC(), "tstatic"]); $func = func::with([SC::class, "->tstatic"]);
self::assertSame(10, $func->invoke()); 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"]); $func = func::with([SC::class, "tmethod"]);
self::assertException(ReflectionException::class, function() use ($func) { self::assertException(ReflectionException::class, function() use ($func) {
$func->invoke(); $func->invoke();
}); });
$func = func::with([new SC(), "tmethod"]); $func = func::with([SC::class, "->tmethod"]);
self::assertSame(11, $func->invoke()); 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());
} }
} }
} }

View File

@ -433,35 +433,54 @@ class func {
############################################################################# #############################################################################
# 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 { static function with($func, bool $strict=true): self {
if (!is_array($func)) {
if ($func instanceof Closure) { if ($func instanceof Closure) {
return new self($func, false, null); return new self(self::TYPE_CLOSURE, $func);
} elseif (self::verifix_function($func, $strict, $reason)) { } elseif ($func instanceof ReflectionFunction) {
return new self($func, false, $reason); 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)) { } elseif (self::verifix_class($func, $strict, $reason)) {
return new self($func, false, $reason); return new self(self::TYPE_CLASS, $func, false, $reason);
} else { } else {
$valid = true; $valid = true;
if (is_array($func) && array_key_exists(0, $func) && is_object($func[0])) { if (is_array($func) && array_key_exists(0, $func) && is_object($func[0])) {
if (self::verifix_method($func, $strict, $bound, $reason)) { if (self::verifix_method($func, $strict, $bound, $reason)) {
$type = self::TYPE_METHOD;
} elseif (self::verifix_static($func, $strict, $bound, $reason)) { } elseif (self::verifix_static($func, $strict, $bound, $reason)) {
$type = self::TYPE_STATIC;
} else { } else {
$valid = false; $valid = false;
} }
} else { } else {
if (self::verifix_static($func, $strict, $bound, $reason)) { if (self::verifix_static($func, $strict, $bound, $reason)) {
$type = self::TYPE_STATIC;
} elseif (self::verifix_method($func, $strict, $bound, $reason)) { } elseif (self::verifix_method($func, $strict, $bound, $reason)) {
$type = self::TYPE_METHOD;
} else { } else {
$valid = false; $valid = false;
} }
} }
if ($valid) return new self($func, $bound, $reason); if ($valid) return new self($type, $func, $bound, $reason);
} }
if ($reason === null) { throw self::not_a_callable($func, $reason);
$msg = var_export($func, true);
$reason = "$msg: not a callable";
}
throw new ValueException($reason);
} }
static function call($func, ...$args) { 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(int $type, $func, bool $bound=false, ?string $reason=null) {
protected function __construct($func, bool $bound=false, ?string $reason=null) {
$object = null; $object = null;
$prefixArgs = []; $prefixArgs = [];
if ($func instanceof Closure) { if (!is_array($func)) {
$type = self::TYPE_CLOSURE;
$object = $func;
$reflection = new ReflectionFunction($func);
} elseif ($func instanceof ReflectionFunction) {
$type = self::TYPE_SIMPLE;
$reflection = $func; $reflection = $func;
} elseif ($func instanceof ReflectionClass) { $func = null;
$type = self::TYPE_CLASS; } else {
$reflection = $func;
} elseif ($func instanceof ReflectionMethod) {
$type = self::TYPE_STATIC;
$reflection = $func;
} elseif (is_array($func)) {
if (count($func) > 2) { if (count($func) > 2) {
$prefixArgs = array_slice($func, 2); $prefixArgs = array_slice($func, 2);
$func = array_slice($func, 0, 2); $func = array_slice($func, 0, 2);
} }
[$c, $f] = $func; [$c, $f] = $func;
if ($c === false) { switch ($type) {
# fonction simple case self::TYPE_SIMPLE:
$type = self::TYPE_SIMPLE;
$reflection = new ReflectionFunction($f); $reflection = new ReflectionFunction($f);
} elseif ($f === false) { break;
# classe case self::TYPE_CLASS:
$type = self::TYPE_CLASS;
$reflection = new ReflectionClass($c); $reflection = new ReflectionClass($c);
} elseif ($c !== null) { break;
# methode case self::TYPE_STATIC:
case self::TYPE_METHOD:
if ($c === null) {
$reflection = null;
} else {
$reflection = new ReflectionMethod($c, $f); $reflection = new ReflectionMethod($c, $f);
if (is_object($c)) { if (is_object($c)) $object = $c;
$type = self::TYPE_METHOD;
$object = $c;
} else {
$type = self::TYPE_STATIC;
} }
} else { break;
throw new StateException("invalid func"); 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(); $constructor = $reflection->getConstructor();
if ($constructor === null) { if ($constructor === null) {
$variadic = false; $variadic = false;
@ -527,42 +566,36 @@ class func {
$minArgs = $constructor->getNumberOfRequiredParameters(); $minArgs = $constructor->getNumberOfRequiredParameters();
$maxArgs = $constructor->getNumberOfParameters(); $maxArgs = $constructor->getNumberOfParameters();
} }
} else { } elseif ($reflection !== null) {
$variadic = $reflection->isVariadic(); $variadic = $reflection->isVariadic();
$minArgs = $reflection->getNumberOfRequiredParameters(); $minArgs = $reflection->getNumberOfRequiredParameters();
$maxArgs = $reflection->getNumberOfParameters(); $maxArgs = $reflection->getNumberOfParameters();
} }
$this->type = $type;
$this->reflection = $reflection; $this->reflection = $reflection;
$this->variadic = $variadic; $this->variadic = $variadic;
$this->minArgs = $minArgs; $this->minArgs = $minArgs;
$this->maxArgs = $maxArgs; $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->object = $object;
$this->prefixArgs = $prefixArgs; $this->bound = true;
} }
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 {
} }
return $this;
function bindClass($objectOrClass, bool $rebind=false): self {
} }
function invoke(?array $args=null) { function invoke(?array $args=null) {
@ -574,7 +607,7 @@ class func {
switch ($this->type) { switch ($this->type) {
case self::TYPE_CLOSURE: case self::TYPE_CLOSURE:
/** @var Closure $closure */ /** @var Closure $closure */
$closure = $this->object; $closure = $this->reflection;
return $closure(...$args); return $closure(...$args);
case self::TYPE_SIMPLE: case self::TYPE_SIMPLE:
/** @var ReflectionFunction $function */ /** @var ReflectionFunction $function */
@ -584,11 +617,14 @@ class func {
case self::TYPE_METHOD: case self::TYPE_METHOD:
/** @var ReflectionMethod $method */ /** @var ReflectionMethod $method */
$method = $this->reflection; $method = $this->reflection;
if ($method === null) throw self::not_a_callable($this->func, $this->reason);
return $method->invoke($this->object, ...$args); return $method->invoke($this->object, ...$args);
case self::TYPE_CLASS: case self::TYPE_CLASS:
/** @var ReflectionClass $class */ /** @var ReflectionClass $class */
$class = $this->reflection; $class = $this->reflection;
return $class->newInstance(...$args); return $class->newInstance(...$args);
default:
throw StateException::unexpected_state();
} }
} }
} }