PHP Classes

File: src/Parameters.php

Recommend this page to a friend!
  Classes of Rodolfo Berrios Arce   Parameter   src/Parameters.php   Download  
File: src/Parameters.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,926 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;

use
BadMethodCallException;
use
Chevere\DataStructure\Interfaces\VectorInterface;
use
Chevere\DataStructure\Map;
use
Chevere\DataStructure\Traits\MapTrait;
use
Chevere\DataStructure\Vector;
use
Chevere\Parameter\Interfaces\ArgumentsInterface;
use
Chevere\Parameter\Interfaces\ParameterCastInterface;
use
Chevere\Parameter\Interfaces\ParameterInterface;
use
Chevere\Parameter\Interfaces\ParametersInterface;
use
InvalidArgumentException;
use
OverflowException;
use function
Chevere\Message\message;

final class
Parameters implements ParametersInterface
{
   
/**
     * @template-use MapTrait<ParameterInterface>
     */
   
use MapTrait;

   
/**
     * @var Map<ParameterInterface>
     */
   
private Map $map;

   
/**
     * @var Vector<string>
     */
   
private VectorInterface $requiredKeys;

   
/**
     * @var Vector<string>
     */
   
private VectorInterface $optionalKeys;

    private
int $optionalMinimum = 0;

    private
bool $isVariadic = false;

   
/**
     * @param ParameterInterface $parameter Required parameters
     */
   
public function __construct(ParameterInterface ...$parameter)
    {
       
$this->map = new Map();
       
$this->requiredKeys = new Vector();
       
$this->optionalKeys = new Vector();
        foreach (
$parameter as $name => $item) {
           
$name = strval($name);
           
$this->addProperty('requiredKeys', $name, $item);
        }
    }

    public function
__invoke(mixed ...$argument): ArgumentsInterface
   
{
        return new
Arguments($this, $argument);
    }

    public function
withIsVariadic(bool $flag): ParametersInterface
   
{
       
$new = clone $this;
       
$new->isVariadic = $flag;

        return
$new;
    }

    public function
isVariadic(): bool
   
{
        return
$this->isVariadic;
    }

    public function
withRequired(string $name, ParameterInterface $parameter): ParametersInterface
   
{
       
$new = clone $this;
       
$new->addProperty('requiredKeys', $name, $parameter);

        return
$new;
    }

    public function
withOptional(string $name, ParameterInterface $parameter): ParametersInterface
   
{
       
$new = clone $this;
       
$new->addProperty('optionalKeys', $name, $parameter);

        return
$new;
    }

    public function
withMakeOptional(string ...$name): ParametersInterface
   
{
       
$new = clone $this;
        if (
$name === []) {
           
$name = $this->requiredKeys->toArray();
        }
        foreach (
$name as $key) {
            if (!
$new->requiredKeys->contains($key)) {
                throw new
InvalidArgumentException(
                    (string)
message(
                       
'Parameter `%name%` is not required',
                       
name: $key
                   
)
                );
            }
           
$parameter = $new->get($key);
           
$new->remove($key);
           
$new->addProperty('optionalKeys', $key, $parameter);
        }

        return
$new;
    }

    public function
withMakeRequired(string ...$name): ParametersInterface
   
{
       
$new = clone $this;
        if (
$name === []) {
           
$name = $this->optionalKeys->toArray();
        }
        foreach (
$name as $key) {
            if (!
$new->optionalKeys()->contains($key)) {
                throw new
InvalidArgumentException(
                    (string)
message(
                       
'Parameter `%name%` is not optional',
                       
name: $key
                   
)
                );
            }
           
$parameter = $new->get($key);
           
$new->remove($key);
           
$new->addProperty('requiredKeys', $key, $parameter);
        }

        return
$new;
    }

    public function
without(string ...$name): ParametersInterface
   
{
       
$new = clone $this;
       
$new->remove(...$name);

        return
$new;
    }

    public function
withMerge(ParametersInterface $parameters): ParametersInterface
   
{
       
$new = clone $this;
        foreach (
$parameters as $name => $parameter) {
           
$container = match ($parameters->requiredKeys()->contains($name)) {
               
true => 'requiredKeys',
                default =>
'optionalKeys',
            };
           
$new->addProperty($container, $name, $parameter);
        }

        return
$new;
    }

    public function
withOptionalMinimum(int $count): ParametersInterface
   
{
       
match (true) {
           
$count < 0 => throw new InvalidArgumentException(
                (string)
message('Count must be greater or equal to 0')
            ),
           
$this->optionalKeys()->count() === 0 => throw new BadMethodCallException(
                (string)
message('No optional parameters found')
            ),
            default =>
null,
        };
       
$new = clone $this;
       
$new->optionalMinimum = $count;
       
$new->assertMinimumOptional();

        return
$new;
    }

    public function
requiredKeys(): VectorInterface
   
{
        return
$this->requiredKeys;
    }

    public function
optionalKeys(): VectorInterface
   
{
        return
$this->optionalKeys;
    }

    public function
optionalMinimum(): int
   
{
        return
$this->optionalMinimum;
    }

    public function
assertHas(string ...$name): void
   
{
       
$this->map->assertHas(...$name);
    }

    public function
has(string ...$name): bool
   
{
        return
$this->map->has(...$name);
    }

    public function
get(string $name): ParameterInterface
   
{
        return
$this->map->get($name);
    }

    public function
required(string $name): ParameterCastInterface
   
{
       
$parameter = $this->get($name);
        if (
$this->optionalKeys()->contains($name)) {
            throw new
InvalidArgumentException(
                (string)
message(
                   
'Parameter `%name%` is optional',
                   
name: $name
               
)
            );
        }

        return new
ParameterCast($parameter);
    }

    public function
optional(string $name): ParameterCastInterface
   
{
       
$parameter = $this->get($name);
        if (!
$this->optionalKeys()->contains($name)) {
            throw new
InvalidArgumentException(
                (string)
message(
                   
'Parameter `%name%` is required',
                   
name: $name
               
)
            );
        }

        return new
ParameterCast($parameter);
    }

    private function
remove(string ...$name): void
   
{
       
$this->map = $this->map->without(...$name);
       
$requiredDiff = array_diff($this->requiredKeys->toArray(), $name);
       
$optionalDiff = array_diff($this->optionalKeys->toArray(), $name);
       
$this->requiredKeys = new Vector(...$requiredDiff);
       
$this->optionalKeys = new Vector(...$optionalDiff);
       
$this->assertMinimumOptional();
    }

    private function
assertMinimumOptional(): void
   
{
        if (
$this->optionalMinimum > $this->optionalKeys()->count()) {
            throw new
InvalidArgumentException(
                (string)
message(
                   
'Count must be less or equal to `%optional%`',
                   
optional: strval($this->optionalMinimum)
                )
            );
        }
    }

    private function
addProperty(string $property, string $name, ParameterInterface $parameter): void
   
{
        if (
$this->has($name)) {
            throw new
OverflowException(
                (string)
message(
                   
'Parameter `%name%` has been already added',
                   
name: $name
               
)
            );
        }
       
$this->{$property} = $this->{$property}->withPush($name);
       
$this->map = $this->map->withPut($name, $parameter);
    }
}