refactor: susun semula struktur folder — Laravel source ke src/
This commit is contained in:
392
vendor/league/csv/src/Serializer/CallbackCasting.php
vendored
Normal file
392
vendor/league/csv/src/Serializer/CallbackCasting.php
vendored
Normal file
@@ -0,0 +1,392 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv\Serializer;
|
||||
|
||||
use Closure;
|
||||
use Deprecated;
|
||||
use ReflectionClass;
|
||||
use ReflectionNamedType;
|
||||
use ReflectionParameter;
|
||||
use ReflectionProperty;
|
||||
use ReflectionType;
|
||||
use ReflectionUnionType;
|
||||
use Throwable;
|
||||
|
||||
use function array_key_exists;
|
||||
use function class_exists;
|
||||
|
||||
/**
|
||||
* @internal Container for registering Closure as type and/or type alias casting
|
||||
* @template TValue
|
||||
*/
|
||||
final class CallbackCasting implements TypeCasting
|
||||
{
|
||||
/** @var array<string, Closure(mixed, bool, mixed...): mixed> */
|
||||
private static array $types = [];
|
||||
/** @var array<string, array<string, Closure(mixed, bool, mixed...): mixed>> */
|
||||
private static array $aliases = [];
|
||||
|
||||
private string $type;
|
||||
private readonly bool $isNullable;
|
||||
/** @var Closure(mixed, bool, mixed...): mixed */
|
||||
private Closure $callback;
|
||||
private array $options = [];
|
||||
private string $message;
|
||||
private readonly TypeCastingInfo $info;
|
||||
|
||||
public function __construct(
|
||||
ReflectionProperty|ReflectionParameter $reflectionProperty,
|
||||
private readonly ?string $alias = null
|
||||
) {
|
||||
[$this->type, $this->isNullable] = self::resolve($reflectionProperty);
|
||||
|
||||
$this->message = match (true) {
|
||||
$reflectionProperty instanceof ReflectionParameter => 'The method `'.$reflectionProperty->getDeclaringClass()?->getName().'::'.$reflectionProperty->getDeclaringFunction()->getName().'` argument `'.$reflectionProperty->getName().'` must be typed with a supported type.',
|
||||
$reflectionProperty instanceof ReflectionProperty => 'The property `'.$reflectionProperty->getDeclaringClass()->getName().'::'.$reflectionProperty->getName().'` must be typed with a supported type.',
|
||||
};
|
||||
|
||||
$this->callback = fn (mixed $value, bool $isNullable, mixed ...$arguments): mixed => $value;
|
||||
$this->info = TypeCastingInfo::fromAccessor($reflectionProperty);
|
||||
}
|
||||
|
||||
public function info(): TypeCastingInfo
|
||||
{
|
||||
return $this->info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws MappingFailed
|
||||
*/
|
||||
public function setOptions(?string $type = null, mixed ...$options): void
|
||||
{
|
||||
if (null === $this->alias) {
|
||||
if (Type::Mixed->value === $this->type && null !== $type) {
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->callback = self::resolveTypeCallback($this->type); /* @phpstan-ignore-line */
|
||||
$this->options = $options;
|
||||
|
||||
return;
|
||||
} catch (Throwable) {
|
||||
|
||||
}
|
||||
|
||||
throw new MappingFailed($this->message);
|
||||
}
|
||||
|
||||
if (Type::Mixed->value === $this->type) {
|
||||
$this->type = self::aliases()[$this->alias];
|
||||
}
|
||||
|
||||
$this->callback = self::resolveAliasCallback($this->type, $this->alias);
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TValue
|
||||
*/
|
||||
public function toVariable(mixed $value): mixed
|
||||
{
|
||||
try {
|
||||
return ($this->callback)($value, $this->isNullable, ...$this->options);
|
||||
} catch (Throwable $exception) {
|
||||
! $exception instanceof TypeCastingFailed || throw $exception;
|
||||
null !== $value || throw TypeCastingFailed::dueToNotNullableType($this->type, $exception, $this->info);
|
||||
|
||||
throw TypeCastingFailed::dueToInvalidValue(match (true) {
|
||||
'' === $value => 'empty string',
|
||||
default => $value,
|
||||
}, $this->type, $exception, $this->info);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Closure(mixed, bool, mixed...): TValue $callback
|
||||
*/
|
||||
public static function register(string $type, Closure $callback, ?string $alias = null): void
|
||||
{
|
||||
if (null === $alias) {
|
||||
self::$types[$type] = match (true) {
|
||||
class_exists($type),
|
||||
interface_exists($type),
|
||||
Type::tryFrom($type) instanceof Type => $callback,
|
||||
default => throw new MappingFailed('The `'.$type.'` could not be register.'),
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
1 === preg_match('/^@\w+$/', $alias) || throw new MappingFailed("The alias `$alias` is invalid. It must start with an `@` character and contain alphanumeric (letters, numbers, regardless of case) plus underscore (_).");
|
||||
|
||||
foreach (self::$aliases as $aliases) {
|
||||
foreach ($aliases as $registeredAlias => $__) {
|
||||
$alias !== $registeredAlias || throw new MappingFailed("The alias `$alias` is already registered. Please choose another name.");
|
||||
}
|
||||
}
|
||||
|
||||
self::$aliases[$type][$alias] = match (true) {
|
||||
class_exists($type),
|
||||
interface_exists($type),
|
||||
Type::tryFrom($type) instanceof Type => $callback,
|
||||
default => throw new MappingFailed('The `'.$type.'` could not be register.'),
|
||||
};
|
||||
}
|
||||
|
||||
public static function unregisterType(string $type): bool
|
||||
{
|
||||
if (!array_key_exists($type, self::$types)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unset(self::$types[$type]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function unregisterTypes(): void
|
||||
{
|
||||
self::$types = [];
|
||||
}
|
||||
|
||||
public static function unregisterAlias(string $alias): bool
|
||||
{
|
||||
if (1 !== preg_match('/^@\w+$/', $alias)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (self::$aliases as $type => $aliases) {
|
||||
foreach ($aliases as $registeredAlias => $__) {
|
||||
if ($registeredAlias === $alias) {
|
||||
unset(self::$aliases[$type][$registeredAlias]);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function unregisterAliases(): void
|
||||
{
|
||||
self::$aliases = [];
|
||||
}
|
||||
|
||||
public static function unregisterAll(): void
|
||||
{
|
||||
self::unregisterTypes();
|
||||
self::unregisterAliases();
|
||||
}
|
||||
|
||||
public static function supportsAlias(?string $alias): bool
|
||||
{
|
||||
return null !== $alias && array_key_exists($alias, self::aliases());
|
||||
}
|
||||
|
||||
public static function supportsType(?string $type): bool
|
||||
{
|
||||
if (null === $type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
self::resolveTypeCallback($type); /* @phpstan-ignore-line */
|
||||
|
||||
return true;
|
||||
} catch (Throwable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string>
|
||||
*/
|
||||
public static function types(): array
|
||||
{
|
||||
return array_keys(self::$types);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public static function aliases(): array
|
||||
{
|
||||
$res = [];
|
||||
foreach (self::$aliases as $registeredType => $aliases) {
|
||||
foreach ($aliases as $registeredAlias => $__) {
|
||||
$res[$registeredAlias] = $registeredType;
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
public static function supports(ReflectionParameter|ReflectionProperty $reflectionProperty, ?string $alias = null): bool
|
||||
{
|
||||
$propertyTypeList = self::getTypes($reflectionProperty->getType());
|
||||
if ([] === $propertyTypeList && self::supportsAlias($alias)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($propertyTypeList as $propertyType) {
|
||||
$type = $propertyType->getName();
|
||||
if (null === $alias) {
|
||||
if (self::supportsType($type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (self::aliasSupportsType($type) || (Type::Mixed->value === $type && self::supportsAlias($alias))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function aliasSupportsType(string $type): bool
|
||||
{
|
||||
foreach (self::aliases() as $registeredType) {
|
||||
if ($type === $registeredType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
if ((new ReflectionClass($type))->implementsInterface($registeredType)) { /* @phpstan-ignore-line */
|
||||
return true;
|
||||
}
|
||||
} catch (Throwable) {
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string $type
|
||||
*/
|
||||
private static function resolveTypeCallback(string $type): Closure
|
||||
{
|
||||
foreach (self::$types as $registeredType => $callback) {
|
||||
if ($type === $registeredType) {
|
||||
return $callback;
|
||||
}
|
||||
|
||||
try {
|
||||
$reflType = new ReflectionClass($type);
|
||||
if ($reflType->implementsInterface($registeredType)) {
|
||||
return $callback;
|
||||
}
|
||||
} catch (Throwable) {
|
||||
}
|
||||
}
|
||||
|
||||
throw new MappingFailed('The `'.$type.'` could not be resolved.');
|
||||
}
|
||||
|
||||
private static function resolveAliasCallback(string $type, string $alias): Closure
|
||||
{
|
||||
$rType = self::aliases()[$alias] ?? null;
|
||||
if (isset($rType)) {
|
||||
return self::$aliases[$rType][$alias];
|
||||
}
|
||||
|
||||
foreach (self::aliases() as $aliasName => $registeredType) {
|
||||
try {
|
||||
$reflType = new ReflectionClass($type); /* @phpstan-ignore-line */
|
||||
if ($reflType->implementsInterface($registeredType)) {
|
||||
return self::$aliases[$registeredType][$aliasName];
|
||||
}
|
||||
} catch (Throwable) {
|
||||
}
|
||||
}
|
||||
|
||||
throw new MappingFailed('The `'.$type.'` could not be resolved.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws MappingFailed
|
||||
*
|
||||
* @return array{0:string, 1:bool}
|
||||
*/
|
||||
private static function resolve(ReflectionParameter|ReflectionProperty $reflectionProperty): array
|
||||
{
|
||||
if (null === $reflectionProperty->getType()) {
|
||||
return [Type::Mixed->value, true];
|
||||
}
|
||||
|
||||
$types = self::getTypes($reflectionProperty->getType());
|
||||
|
||||
$type = null;
|
||||
$isNullable = false;
|
||||
$hasMixed = false;
|
||||
foreach ($types as $foundType) {
|
||||
if (!$isNullable && $foundType->allowsNull()) {
|
||||
$isNullable = true;
|
||||
}
|
||||
|
||||
if (null === $type) {
|
||||
$instanceName = $foundType->getName();
|
||||
if (self::supportsType($instanceName) || array_key_exists($instanceName, self::$aliases)) {
|
||||
$type = $foundType;
|
||||
}
|
||||
|
||||
if (true !== $hasMixed && Type::Mixed->value === $instanceName) {
|
||||
$hasMixed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return match (true) {
|
||||
$type instanceof ReflectionNamedType => [$type->getName(), $isNullable],
|
||||
$hasMixed => [Type::Mixed->value, true],
|
||||
default => throw new MappingFailed(match (true) {
|
||||
$reflectionProperty instanceof ReflectionParameter => 'The method `'.$reflectionProperty->getDeclaringClass()?->getName().'::'.$reflectionProperty->getDeclaringFunction()->getName().'` argument `'.$reflectionProperty->getName().'` must be typed with a supported type.',
|
||||
$reflectionProperty instanceof ReflectionProperty => 'The property `'.$reflectionProperty->getDeclaringClass()->getName().'::'.$reflectionProperty->getName().'` must be typed with a supported type.',
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<ReflectionNamedType>
|
||||
*/
|
||||
private static function getTypes(?ReflectionType $type): array
|
||||
{
|
||||
return match (true) {
|
||||
$type instanceof ReflectionNamedType => [$type],
|
||||
$type instanceof ReflectionUnionType => array_filter(
|
||||
$type->getTypes(),
|
||||
fn (ReflectionType $innerType) => $innerType instanceof ReflectionNamedType
|
||||
),
|
||||
default => [],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATION WARNING! This method will be removed in the next major point release.
|
||||
*
|
||||
* @deprecated since version 9.13.0
|
||||
* @see CallbackCasting::unregisterType()
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
#[Deprecated(message:'use League\Csv\Serializer\CallbackCasting::unregisterType() instead', since:'league/csv:9.13.0')]
|
||||
public static function unregister(string $type): bool
|
||||
{
|
||||
return self::unregisterType($type);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user