<?php 
 
/* 
 * This file is part of Chevere. 
 * 
 * (c) Rodolfo Berrios <[email protected]> 
 * 
 * For the full copyright and license information, please view the LICENSE 
 * file that was distributed with this source code. 
 */ 
 
declare(strict_types=1); 
 
namespace Chevere\Parameter\Attributes; 
 
use Chevere\Parameter\Exceptions\ParameterException; 
use Chevere\Parameter\Interfaces\ArgumentsInterface; 
use Chevere\Parameter\Interfaces\ParameterInterface; 
use LogicException; 
use ReflectionFunction; 
use ReflectionMethod; 
use Throwable; 
use function Chevere\Message\message; 
use function Chevere\Parameter\parameterAttr; 
use function Chevere\Parameter\reflectionToParameters; 
 
function stringAttr(string $name): StringAttr 
{ 
    $caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]; 
 
    // @phpstan-ignore-next-line 
    return parameterAttr($name, $caller['function'], $caller['class'] ?? ''); 
} 
 
function enumAttr(string $name): EnumAttr 
{ 
    $caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]; 
 
    // @phpstan-ignore-next-line 
    return parameterAttr($name, $caller['function'], $caller['class'] ?? ''); 
} 
 
function intAttr(string $name): IntAttr 
{ 
    $caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]; 
 
    // @phpstan-ignore-next-line 
    return parameterAttr($name, $caller['function'], $caller['class'] ?? ''); 
} 
 
function floatAttr(string $name): FloatAttr 
{ 
    $caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]; 
 
    // @phpstan-ignore-next-line 
    return parameterAttr($name, $caller['function'], $caller['class'] ?? ''); 
} 
 
function boolAttr(string $name): BoolAttr 
{ 
    $caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]; 
 
    // @phpstan-ignore-next-line 
    return parameterAttr($name, $caller['function'], $caller['class'] ?? ''); 
} 
 
function nullAttr(string $name): NullAttr 
{ 
    $caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]; 
 
    // @phpstan-ignore-next-line 
    return parameterAttr($name, $caller['function'], $caller['class'] ?? ''); 
} 
 
function arrayAttr(string $name): ArrayAttr 
{ 
    $caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]; 
 
    // @phpstan-ignore-next-line 
    return parameterAttr($name, $caller['function'], $caller['class'] ?? ''); 
} 
 
function iteratorAttr(string $name): IterableAttr 
{ 
    $caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]; 
 
    // @phpstan-ignore-next-line 
    return parameterAttr($name, $caller['function'], $caller['class'] ?? ''); 
} 
 
function unionAttr(string $name): UnionAttr 
{ 
    $caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]; 
 
    // @phpstan-ignore-next-line 
    return parameterAttr($name, $caller['function'], $caller['class'] ?? ''); 
} 
 
function returnAttr(): ReturnAttr 
{ 
    $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); 
    $caller = $trace[1]; 
    $class = $caller['class'] ?? null; 
    $method = $caller['function']; 
    $convention = "{$class}::return"; 
    $reflection = $class 
        ? new ReflectionMethod($class, $method) 
        : new ReflectionFunction($method); 
    $attribute = $reflection->getAttributes(ReturnAttr::class)[0] ?? null; 
    if ($attribute === null) { 
        if (! is_callable($convention)) { 
            throw new LogicException( 
                (string) message( 
                    'No applicable return rules to validate', 
                ) 
            ); 
        } 
        $parameter = $convention(); 
        if (! $parameter instanceof ParameterInterface) { 
            throw new LogicException( 
                (string) message( 
                    'Callable `%callable%` must return a `%type%` instance', 
                    callable: $convention, 
                    type: ParameterInterface::class 
                ) 
            ); 
        } 
    } else { 
        $attribute = $attribute->newInstance(); 
    } 
 
    /** @var ReturnAttr $attribute */ 
    return $attribute; 
} 
 
/** 
 * Get Arguments for an array parameter. 
 */ 
function arrayArguments(string $name): ArgumentsInterface 
{ 
    $caller = debug_backtrace(0, 2)[1]; 
    $class = $caller['class'] ?? false; 
    $method = $caller['function']; 
    $args = $caller['args'] ?? []; 
    $reflection = $class 
        ? new ReflectionMethod($class, $method) 
        : new ReflectionFunction($method); 
    $parameters = reflectionToParameters($reflection); 
    $parameters->assertHas($name); 
    $array = match ($parameters->optionalKeys()->contains($name)) { 
        true => $parameters->optional($name)->array(), 
        default => $parameters->required($name)->array(), 
    }; 
    $pos = -1; 
    $arguments = []; 
    foreach ($parameters->keys() as $named) { 
        $pos++; 
        $arguments[$named] = match (true) { 
            array_key_exists($pos, $args) => $args[$pos], 
            default => $parameters->get($named)->default(), 
        }; 
    } 
 
    return $array->parameters()->__invoke(...$arguments[$name]); 
} 
 
/** 
 * Validates argument `$name` against parameter attribute rules. 
 * 
 * @param ?string $name Argument name or `null` to validate all arguments. 
 */ 
function valid(?string $name = null): void 
{ 
    $trace = debug_backtrace(0, 2); 
    $caller = $trace[1]; 
    $class = $caller['class'] ?? false; 
    $method = $caller['function']; 
    $args = $caller['args'] ?? []; 
    $reflection = $class 
        ? new ReflectionMethod($class, $method) 
        : new ReflectionFunction($method); 
    $parameters = reflectionToParameters($reflection); 
    $pos = -1; 
    $arguments = []; 
    foreach ($parameters->keys() as $named) { 
        $pos++; 
        if (! isset($args[$pos])) { 
            continue; 
        } 
        $arguments[$named] = $args[$pos]; 
    } 
    if ($name === null) { 
        $parameters(...$arguments); 
 
        return; 
    } 
    if ($parameters->optionalKeys()->contains($name) 
        && ! array_key_exists($name, $arguments) 
    ) { 
        return; 
    } 
 
    try { 
        if (! $parameters->has($name)) { 
            throw new LogicException( 
                (string) message( 
                    'Parameter `%name%` not found', 
                    name: $name, 
                ) 
            ); 
        } 
        $parameter = $parameters->get($name); 
        $parameter->__invoke($arguments[$name]); 
    } catch (Throwable $e) { 
        $invoker = $trace[0]; 
        $file = $invoker['file'] ?? 'na'; 
        $line = $invoker['line'] ?? 0; 
 
        throw new ParameterException($e->getMessage(), $e, $file, $line); 
    } 
} 
 
 |