PHP Classes

File: src/Traits/NumericParameterTrait.php

Recommend this page to a friend!
  Classes of Rodolfo Berrios Arce   Parameter   src/Traits/NumericParameterTrait.php   Download  
File: src/Traits/NumericParameterTrait.php
Role: Class source
Content type: text/plain
Description: Class source
Class: Parameter
Validate function parameters with PHP attributes
Author: By
Last change:
Date: 21 days ago
Size: 7,597 bytes
 

Contents

Class file image Download
<?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\Traits;

use
Chevere\Parameter\Interfaces\FloatParameterInterface;
use
Chevere\Parameter\Interfaces\IntParameterInterface;
use
InvalidArgumentException;
use
OverflowException;
use function
Chevere\Message\message;
use function
Chevere\Parameter\valMd;

/**
 * @template-covariant TValue
 */
trait NumericParameterTrait
{
   
/**
     * @var array<TValue>
     */
   
private array $accept = [];

   
/**
     * @var array<TValue>
     */
   
private array $reject = [];

    private
bool $isSensitive = false;

    private function
errorOverflow(string $property, string $topic): string
   
{
        return (string)
message(
           
'Cannot set %property% value when %topic% range is set',
           
property: $property,
           
topic: $topic,
        );
    }

    private function
errorMinMaxArgument(string $target, string $conflict): string
   
{
        return (string)
message(
           
'Cannot set %target% value greater or equal than %conflict% value',
           
target: $target,
           
conflict: $conflict,
        );
    }

    private function
assertAcceptEmpty(string $message): void
   
{
        if (
$this->accept !== []) {
            throw new
OverflowException($message);
        }
    }

    private function
assertRejectEmpty(string $message): void
   
{
        if (
$this->reject !== []) {
            throw new
OverflowException($message);
        }
    }

    private function
setMin(int|float $value, int|float $max): void
   
{
       
$this->assertAcceptEmpty(
           
$this->errorOverflow('min', 'accept')
        );
       
$this->assertRejectEmpty(
           
$this->errorOverflow('min', 'reject')
        );
        if (
$value >= ($this->max ?? $max)) {
            throw new
InvalidArgumentException(
               
$this->errorMinMaxArgument('min', 'max')
            );
        }
       
// @phpstan-ignore-next-line
       
$this->min = $value;
    }

    private function
setMax(int|float $value, int|float $min): void
   
{
       
$this->assertAcceptEmpty(
           
$this->errorOverflow('max', 'accept')
        );
       
$this->assertRejectEmpty(
           
$this->errorOverflow('min', 'reject')
        );
        if (
$value <= ($this->min ?? $min)) {
            throw new
InvalidArgumentException(
               
$this->errorMinMaxArgument('max', 'min')
            );
        }
       
// @phpstan-ignore-next-line
       
$this->max = $value;
    }

    private function
setAccept(int|float ...$value): void
   
{
       
sort($value);
       
// @phpstan-ignore-next-line
       
$this->accept = $value;
       
$this->min = null;
       
$this->max = null;
       
$this->assertAcceptReject();
    }

    private function
assertAcceptReject(): void
   
{
       
$intersect = array_intersect($this->accept, $this->reject);
        if (
$intersect !== []) {
           
$accept = implode(', ', $this->accept);
           
$reject = implode(', ', $this->reject);

            throw new
InvalidArgumentException(
                (string)
message(
                   
'Accept list `%accept%` intersects with reject list `%reject%`',
                   
accept: "[{$accept}]",
                   
reject: "[{$reject}]",
                )
            );
        }
    }

    private function
setReject(int|float ...$value): void
   
{
       
sort($value);
       
// @phpstan-ignore-next-line
       
$this->reject = $value;
       
$this->min = null;
       
$this->max = null;
       
$this->assertAcceptReject();
    }

    private function
assertNumericCompatible(
       
IntParameterInterface|FloatParameterInterface $parameter
   
): void {
       
$this->assertNumericList($parameter, 'accept');
       
$this->assertNumericList($parameter, 'reject');
       
$this->assertNumericLimit($parameter, 'min');
       
$this->assertNumericLimit($parameter, 'max');
    }

    private function
assertNumericList(
       
IntParameterInterface|FloatParameterInterface $parameter,
       
string $property
   
): void {
       
// @infection-ignore-all
       
$diff = array_merge(
           
array_diff($this->{$property}, $parameter->{$property}()),
           
array_diff($parameter->{$property}(), $this->{$property})
        );
        if (
$diff !== []) {
           
$value = implode(', ', $this->{$property});
           
$provided = implode(', ', $parameter->{$property}());

            throw new
InvalidArgumentException(
                (string)
message(
                   
'Expected %topic% values in `%expect%`, provided `%provided%`',
                   
topic: $property,
                   
expect: "[{$value}]",
                   
provided: "[{$provided}]",
                )
            );
        }
    }

    private function
assertNumericLimit(
       
IntParameterInterface|FloatParameterInterface $parameter,
       
string $property
   
): void {
        if (
$this->{$property} !== $parameter->{$property}()) {
           
$value = strval($this->{$property} ?? 'null');
           
$provided = strval($parameter->{$property}() ?? 'null');

            throw new
InvalidArgumentException(
                (string)
message(
                   
'Expected %topic% value `%value%`, provided `%provided%`',
                   
topic: $property,
                   
value: $value,
                   
provided: $provided,
                )
            );
        }
    }

   
/**
     * @return TValue
     */
   
private function assert(
       
int|float $argument
   
): int|float {
        if (
$this->accept !== []) {
            if (
in_array($argument, $this->accept, true)) {
                return
$argument;
            }
           
$values = implode(',', $this->accept);

            throw new
InvalidArgumentException(
                (string)
message(
                   
'Argument value provided%provided% is not an accepted value in `%value%`',
                   
provided: valMd($argument, $this->isSensitive),
                   
value: "[{$values}]"
               
)
            );
        }
        if (
$this->reject !== []) {
            if (!
in_array($argument, $this->reject, true)) {
                return
$argument;
            }
           
$values = implode(',', $this->reject);

            throw new
InvalidArgumentException(
                (string)
message(
                   
'Argument value provided%provided% is on rejected list `%value%`',
                   
provided: valMd($argument, $this->isSensitive),
                   
value: "[{$values}]"
               
)
            );
        }
        if (
$this->min() !== null && $argument < $this->min()) {
            throw new
InvalidArgumentException(
                (string)
message(
                   
'Argument value provided%provided% is less than `%min%`',
                   
provided: valMd($argument, $this->isSensitive),
                   
min: strval($this->min())
                )
            );
        }
        if (
$this->max !== null && $argument > $this->max) {
            throw new
InvalidArgumentException(
                (string)
message(
                   
'Argument value provided%provided% is greater than `%max%`',
                   
provided: valMd($argument, $this->isSensitive),
                   
max: strval($this->max)
                )
            );
        }

        return
$argument;
    }
}