| Current Path : /srv/web/sites/trentinoplant.it/httpdocs/vendor1/laminas/laminas-validator/src/ |
| Current File : /srv/web/sites/trentinoplant.it/httpdocs/vendor1/laminas/laminas-validator/src/DateComparison.php |
<?php
declare(strict_types=1);
namespace Laminas\Validator;
use DateTimeImmutable;
use DateTimeInterface;
use DateTimeZone;
use Laminas\Validator\Exception\InvalidArgumentException;
use function assert;
use function get_debug_type;
use function is_string;
use function preg_match;
/**
* @psalm-type OptionsArgument = array{
* min?: string|DateTimeInterface|null,
* max?: string|DateTimeInterface|null,
* inclusiveMin?: bool,
* inclusiveMax?: bool,
* inputFormat?: string|null,
* }
*/
final class DateComparison extends AbstractValidator
{
public const ERROR_INVALID_TYPE = 'invalidType';
public const ERROR_INVALID_DATE = 'invalidDate';
public const ERROR_NOT_GREATER_INCLUSIVE = 'notGreaterInclusive';
public const ERROR_NOT_GREATER = 'notGreater';
public const ERROR_NOT_LESS_INCLUSIVE = 'notLessInclusive';
public const ERROR_NOT_LESS = 'notLess';
/** @var array<string, string> */
protected array $messageTemplates = [
self::ERROR_INVALID_TYPE => 'Expected a string or a date time instance but received "%type"',
self::ERROR_INVALID_DATE => 'Invalid date provided',
self::ERROR_NOT_GREATER_INCLUSIVE => 'A date equal to or after %min% is required',
self::ERROR_NOT_GREATER => 'A date after %min% is required',
self::ERROR_NOT_LESS_INCLUSIVE => 'A date equal to or before %max% is required',
self::ERROR_NOT_LESS => 'A date before %max% is required',
];
/** @var array<string, string> */
protected array $messageVariables = [
'type' => 'type',
'min' => 'minString',
'max' => 'maxString',
];
private readonly ?DateTimeInterface $min;
private readonly ?DateTimeInterface $max;
private readonly bool $inclusiveMin;
private readonly bool $inclusiveMax;
private readonly ?string $inputFormat;
/** Input type used in message variables */
protected ?string $type = null;
protected ?string $minString = null;
protected ?string $maxString = null;
/** @param OptionsArgument $options */
public function __construct(array $options = [])
{
parent::__construct($options);
$this->min = $this->dateInstanceBound($options['min'] ?? null);
$this->max = $this->dateInstanceBound($options['max'] ?? null);
$this->inclusiveMin = $options['inclusiveMin'] ?? true;
$this->inclusiveMax = $options['inclusiveMax'] ?? true;
$this->inputFormat = $options['inputFormat'] ?? null;
if ($this->min === null && $this->max === null) {
throw new InvalidArgumentException(
'At least one date boundary must be supplied',
);
}
$outputFormat = $this->inputFormat ?? 'jS F Y H:i:s';
if ($this->min !== null) {
$this->minString = $this->min->format($outputFormat);
}
if ($this->max !== null) {
$this->maxString = $this->max->format($outputFormat);
}
}
public function isValid(mixed $value): bool
{
$this->type = get_debug_type($value);
$this->value = $value;
if (! is_string($value) && ! $value instanceof DateTimeInterface) {
$this->error(self::ERROR_INVALID_TYPE);
return false;
}
$date = $this->valueToDate($value);
if ($date === null) {
$this->error(self::ERROR_INVALID_DATE);
return false;
}
if ($this->min !== null && $this->inclusiveMin && $date < $this->min) {
$this->error(self::ERROR_NOT_GREATER_INCLUSIVE);
return false;
}
if ($this->min !== null && ! $this->inclusiveMin && $date <= $this->min) {
$this->error(self::ERROR_NOT_GREATER);
return false;
}
if ($this->max !== null && $this->inclusiveMax && $date > $this->max) {
$this->error(self::ERROR_NOT_LESS_INCLUSIVE);
return false;
}
if ($this->max !== null && ! $this->inclusiveMax && $date >= $this->max) {
$this->error(self::ERROR_NOT_LESS);
return false;
}
return true;
}
private function valueToDate(string|DateTimeInterface $input): DateTimeInterface|null
{
if ($input instanceof DateTimeInterface) {
return $this->w3cDateFromString($input->format('Y-m-d\TH:i:s'));
}
if ($this->inputFormat !== null) {
$date = DateTimeImmutable::createFromFormat($this->inputFormat, $input, new DateTimeZone('UTC'));
if ($date instanceof DateTimeImmutable) {
return $date;
}
}
$date = $this->isoDateFromString($input);
if ($date !== null) {
return $date;
}
$date = $this->w3cDateFromString($input);
if ($date !== null) {
return $date;
}
return null;
}
private function dateInstanceBound(string|DateTimeInterface|null $dateTime): DateTimeInterface|null
{
if ($dateTime instanceof DateTimeInterface) {
return $this->w3cDateFromString($dateTime->format('Y-m-d\TH:i:s'));
}
if ($dateTime === null) {
return null;
}
$date = $this->isoDateFromString($dateTime);
if ($date !== null) {
return $date;
}
$date = $this->w3cDateFromString($dateTime);
if ($date !== null) {
return $date;
}
throw new InvalidArgumentException(
'Min/max date bounds must be either DateTime instances, or a string in one of the formats: '
. '"Y-m-d" for a date or "Y-m-d\TH:i:s" for date time',
);
}
private function isoDateFromString(string $input): DateTimeImmutable|null
{
if (! preg_match('/^\d{4}-[0-1]\d-[0-3]\d$/', $input)) {
return null;
}
$date = DateTimeImmutable::createFromFormat('!Y-m-d', $input, new DateTimeZone('UTC'));
assert($date !== false);
return $date;
}
private function w3cDateFromString(string $input): DateTimeImmutable|null
{
if (! preg_match('/^\d{4}-[0-1]\d-[0-3]\dT\d{1,2}:[0-5]\d:[0-5]\d$/', $input)) {
return null;
}
$date = DateTimeImmutable::createFromFormat('Y-m-d\TH:i:s', $input, new DateTimeZone('UTC'));
assert($date !== false);
return $date;
}
}