130 lines
3.0 KiB
PHP
130 lines
3.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* This file is part of the Carbon package.
|
|
*
|
|
* (c) Brian Nesbitt <brian@nesbot.com>
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE
|
|
* file that was distributed with this source code.
|
|
*/
|
|
|
|
namespace Carbon;
|
|
|
|
use Closure;
|
|
use DateInterval;
|
|
use DatePeriod;
|
|
use DateTime;
|
|
use DateTimeInterface;
|
|
use DateTimeZone;
|
|
use ReflectionFunction;
|
|
use ReflectionNamedType;
|
|
use ReflectionType;
|
|
|
|
final class Callback
|
|
{
|
|
private ?ReflectionFunction $function;
|
|
|
|
private function __construct(private readonly Closure $closure)
|
|
{
|
|
}
|
|
|
|
public static function fromClosure(Closure $closure): self
|
|
{
|
|
return new self($closure);
|
|
}
|
|
|
|
public static function parameter(mixed $closure, mixed $value, string|int $index = 0): mixed
|
|
{
|
|
if ($closure instanceof Closure) {
|
|
return self::fromClosure($closure)->prepareParameter($value, $index);
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
public function getReflectionFunction(): ReflectionFunction
|
|
{
|
|
return $this->function ??= new ReflectionFunction($this->closure);
|
|
}
|
|
|
|
public function prepareParameter(mixed $value, string|int $index = 0): mixed
|
|
{
|
|
$type = $this->getParameterType($index);
|
|
|
|
if (!($type instanceof ReflectionNamedType)) {
|
|
return $value;
|
|
}
|
|
|
|
$name = $type->getName();
|
|
|
|
if ($name === CarbonInterface::class) {
|
|
$name = $value instanceof DateTime ? Carbon::class : CarbonImmutable::class;
|
|
}
|
|
|
|
if (!class_exists($name) || is_a($value, $name)) {
|
|
return $value;
|
|
}
|
|
|
|
$class = $this->getPromotedClass($value);
|
|
|
|
if ($class && is_a($name, $class, true)) {
|
|
return $name::instance($value);
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
public function call(mixed ...$arguments): mixed
|
|
{
|
|
foreach ($arguments as $index => &$value) {
|
|
if ($this->getPromotedClass($value)) {
|
|
$value = $this->prepareParameter($value, $index);
|
|
}
|
|
}
|
|
|
|
return ($this->closure)(...$arguments);
|
|
}
|
|
|
|
private function getParameterType(string|int $index): ?ReflectionType
|
|
{
|
|
$parameters = $this->getReflectionFunction()->getParameters();
|
|
|
|
if (\is_int($index)) {
|
|
return ($parameters[$index] ?? null)?->getType();
|
|
}
|
|
|
|
foreach ($parameters as $parameter) {
|
|
if ($parameter->getName() === $index) {
|
|
return $parameter->getType();
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/** @return class-string|null */
|
|
private function getPromotedClass(mixed $value): ?string
|
|
{
|
|
if ($value instanceof DateTimeInterface) {
|
|
return CarbonInterface::class;
|
|
}
|
|
|
|
if ($value instanceof DateInterval) {
|
|
return CarbonInterval::class;
|
|
}
|
|
|
|
if ($value instanceof DatePeriod) {
|
|
return CarbonPeriod::class;
|
|
}
|
|
|
|
if ($value instanceof DateTimeZone) {
|
|
return CarbonTimeZone::class;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|