modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2024-09-19 08:22:18 +04:00
parent db07658392
commit b7ea1985b3
2 changed files with 98 additions and 49 deletions

View File

@ -2,6 +2,8 @@
namespace nur\sery\php; namespace nur\sery\php;
use Closure; use Closure;
use Exception;
use nur\sery\A;
use nur\sery\cv; use nur\sery\cv;
use nur\sery\StateException; use nur\sery\StateException;
use nur\sery\ValueException; use nur\sery\ValueException;
@ -428,8 +430,14 @@ class func {
############################################################################# #############################################################################
# func # func
const TYPE_MASK = 0b11;
const FLAG_STATIC = 0b100;
const TYPE_CLOSURE = 0, TYPE_FUNCTION = 1, TYPE_CLASS = 2, TYPE_METHOD = 3; const TYPE_CLOSURE = 0, TYPE_FUNCTION = 1, TYPE_CLASS = 2, TYPE_METHOD = 3;
const TYPE_STATIC = self::TYPE_METHOD | self::FLAG_STATIC;
protected static function not_a_callable($func, ?string $reason) { protected static function not_a_callable($func, ?string $reason) {
if ($reason === null) { if ($reason === null) {
$msg = var_export($func, true); $msg = var_export($func, true);
@ -438,53 +446,56 @@ class func {
return new ValueException($reason); return new ValueException($reason);
} }
static function with($func, bool $strict=true): self { static function with($func, ?array $args=null, bool $strict=true): self {
if (!is_array($func)) { if (!is_array($func)) {
if ($func instanceof Closure) { if ($func instanceof Closure) {
return new self(self::TYPE_CLOSURE, $func); return new self(self::TYPE_CLOSURE, $func, $args);
} elseif ($func instanceof ReflectionFunction) { } elseif ($func instanceof ReflectionFunction) {
return new self(self::TYPE_FUNCTION, $func); return new self(self::TYPE_FUNCTION, $func, $args);
} elseif ($func instanceof ReflectionClass) { } elseif ($func instanceof ReflectionClass) {
return new self(self::TYPE_CLASS, $func); return new self(self::TYPE_CLASS, $func, $args);
} elseif ($func instanceof ReflectionMethod) { } elseif ($func instanceof ReflectionMethod) {
return new self(self::TYPE_METHOD, $func, false); return new self(self::TYPE_METHOD, $func, $args, false);
} }
} }
if (self::verifix_function($func, $strict, $reason)) { if (self::verifix_function($func, $strict, $reason)) {
return new self(self::TYPE_FUNCTION, $func, false, $reason); return new self(self::TYPE_FUNCTION, $func, $args, false, $reason);
} elseif (self::verifix_class($func, $strict, $reason)) { } elseif (self::verifix_class($func, $strict, $reason)) {
return new self(self::TYPE_CLASS, $func, false, $reason); return new self(self::TYPE_CLASS, $func, $args, 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)) { } elseif (self::verifix_method($func, $strict, $bound, $reason)) {
$type = self::TYPE_METHOD; return new self(self::TYPE_METHOD, $func, $args, $bound, $reason);
} else { } elseif (self::verifix_static($func, $strict, $bound, $reason)) {
$valid = false; return new self(self::TYPE_STATIC, $func, $args, $bound, $reason);
}
}
if ($valid) return new self($type, $func, $bound, $reason);
} }
throw self::not_a_callable($func, $reason); throw self::not_a_callable($func, $reason);
} }
static function ensure($func, ?array $args=null, bool $strict=true): self {
$func = self::with($func, $args, $strict);
if (!$func->isBound()) {
throw self::not_a_callable($func->func, $func->reason);
}
return $func;
}
static function check($func, ?array $args=null, bool $strict=true): bool {
try {
self::ensure($func, $args, $strict);
return true;
} catch (Exception $e) {
return false;
}
}
static function call($func, ...$args) { static function call($func, ...$args) {
return self::with($func)->invoke($args); return self::with($func)->invoke($args);
} }
############################################################################# #############################################################################
protected function __construct(int $type, $func, bool $bound=false, ?string $reason=null) { protected function __construct(int $type, $func, ?array $args=null, bool $bound=false, ?string $reason=null) {
$flags = $type & ~self::TYPE_MASK;
$type = $type & self::TYPE_MASK;
$object = null; $object = null;
$prefixArgs = []; $prefixArgs = [];
if (!is_array($func)) { if (!is_array($func)) {
@ -515,8 +526,10 @@ class func {
throw StateException::unexpected_state(); throw StateException::unexpected_state();
} }
} }
A::merge($prefixArgs, $args);
$this->type = $type; $this->type = $type;
$this->flags = $flags;
$this->func = $func; $this->func = $func;
$this->bound = $bound; $this->bound = $bound;
$this->reason = $reason; $this->reason = $reason;
@ -527,6 +540,8 @@ class func {
protected int $type; protected int $type;
protected int $flags;
protected ?array $func; protected ?array $func;
protected bool $bound; protected bool $bound;
@ -550,15 +565,19 @@ class func {
$variadic = false; $variadic = false;
$minArgs = $maxArgs = 0; $minArgs = $maxArgs = 0;
if ($reflection instanceof Closure) { if ($reflection instanceof Closure) {
$r = new ReflectionFunction($reflection);
$variadic = $r->isVariadic();
$minArgs = $r->getNumberOfRequiredParameters();
$maxArgs = $r->getNumberOfParameters();
} elseif ($reflection instanceof ReflectionClass) { } elseif ($reflection instanceof ReflectionClass) {
$constructor = $reflection->getConstructor(); $r = $reflection->getConstructor();
if ($constructor === null) { if ($r === null) {
$variadic = false; $variadic = false;
$minArgs = $maxArgs = 0; $minArgs = $maxArgs = 0;
} else { } else {
$variadic = $constructor->isVariadic(); $variadic = $r->isVariadic();
$minArgs = $constructor->getNumberOfRequiredParameters(); $minArgs = $r->getNumberOfRequiredParameters();
$maxArgs = $constructor->getNumberOfParameters(); $maxArgs = $r->getNumberOfParameters();
} }
} elseif ($reflection !== null) { } elseif ($reflection !== null) {
$variadic = $reflection->isVariadic(); $variadic = $reflection->isVariadic();
@ -571,19 +590,25 @@ class func {
$this->maxArgs = $maxArgs; $this->maxArgs = $maxArgs;
} }
function bind($object, bool $rebind=false): self { function isBound(): bool {
if ($this->type !== self::TYPE_METHOD) return true;
if ($this->flags & self::FLAG_STATIC) return $this->bound;
else return $this->bound && $this->object !== null;
}
function bind($object): self {
if ($this->type !== self::TYPE_METHOD) return $this; if ($this->type !== self::TYPE_METHOD) return $this;
$bound = $this->bound && $this->object !== null;
if ($bound && !$rebind) return $this;
[$c, $f] = $this->func; [$c, $f] = $this->func;
if ($this->reflection === null) { if ($this->reflection === null) {
$this->func[0] = $c = $object; $this->func[0] = $c = $object;
$this->updateReflection(new ReflectionMethod($c, $f)); $this->updateReflection(new ReflectionMethod($c, $f));
} }
if (is_string($c) && is_object($object) && !($object instanceof $c)) { if (is_object($object) && !($this->flags & self::FLAG_STATIC)) {
if (is_object($c)) $c = get_class($c);
if (is_string($c) && !($object instanceof $c)) {
throw ValueException::invalid_type($object, $c); throw ValueException::invalid_type($object, $c);
} }
if (is_object($object)) {
$this->object = $object; $this->object = $object;
$this->bound = true; $this->bound = true;
} }

View File

@ -1007,13 +1007,13 @@ namespace nur\sery\php {
$func = func::with(C1::class); $func = func::with(C1::class);
/** @var C1 $i1 */ /** @var C1 $i1 */
$i1 = $func->invoke(); $i1 = $func->invoke();
self::assertInstanceOf(C1::class, $i1); self::assertSame(null, $i1->first); self::assertInstanceOf(C1::class, $i1); self::assertSame(0, $i1->base);
$i1 = $func->invoke([]); $i1 = $func->invoke([]);
self::assertInstanceOf(C1::class, $i1); self::assertSame(null, $i1->first); self::assertInstanceOf(C1::class, $i1); self::assertSame(0, $i1->base);
$i1 = $func->invoke([1]); $i1 = $func->invoke([1]);
self::assertInstanceOf(C1::class, $i1); self::assertSame(1, $i1->first); self::assertInstanceOf(C1::class, $i1); self::assertSame(1, $i1->base);
$i1 = $func->invoke([1, 2]); $i1 = $func->invoke([1, 2]);
self::assertInstanceOf(C1::class, $i1); self::assertSame(1, $i1->first); self::assertInstanceOf(C1::class, $i1); self::assertSame(1, $i1->base);
} }
private static function invoke_asserts(): array { private static function invoke_asserts(): array {
@ -1082,9 +1082,33 @@ namespace nur\sery\php {
self::assertException(ReflectionException::class, $bind_ko([null, "->tmethod"], SC::class)); self::assertException(ReflectionException::class, $bind_ko([null, "->tmethod"], SC::class));
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::assertException(ReflectionException::class, $bind_ko([null, "::tmethod"], $sc));
self::assertSame(11, $bind_ok([null, "->tmethod"], $sc)); self::assertSame(11, $bind_ok([null, "->tmethod"], $sc));
} }
function testArgs() {
$func = function(int $a, int $b, int $c): int {
return $a + $b + $c;
};
self::assertSame(6, func::call($func, 1, 2, 3));
self::assertSame(6, func::call($func, 1, 2, 3, 4));
self::assertSame(6, func::with($func)->invoke([1, 2, 3]));
self::assertSame(6, func::with($func, [1])->invoke([2, 3]));
self::assertSame(6, func::with($func, [1, 2])->invoke([3]));
self::assertSame(6, func::with($func, [1, 2, 3])->invoke());
self::assertSame(6, func::with($func, [1, 2, 3, 4])->invoke());
}
function testRebind() {
$func = func::with([C1::class, "tmethod"]);
self::assertSame(11, $func->bind(new C1(0))->invoke());
self::assertSame(12, $func->bind(new C1(1))->invoke());
self::assertException(ValueException::class, function() use ($func) {
$func->bind(new C0())->invoke();
});
}
} }
} }
@ -1126,18 +1150,18 @@ namespace nur\sery\php\impl {
} }
class C1 { class C1 {
function __construct($first) { function __construct(int $base=0) {
$this->first = $first; $this->base = $base;
} }
public $first; public int $base;
static function tstatic(): int { static function tstatic(): int {
return 10; return 10;
} }
function tmethod(): int { function tmethod(): int {
return 11; return 11 + $this->base;
} }
} }
} }