<?php
declare(strict_types=1);

// Save as configured-validator.php in a Composer project.
// composer require symfony/validator:7.4.23
// php configured-validator.php request.json
require __DIR__ . '/vendor/autoload.php';

use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Validation;

final class ConfiguredValue
{
    private const RULES = array (
  'type' => 'email',
  'required' => true,
  'min' => 5,
  'max' => 180,
);
    public function __construct(public mixed $value) {}

    #[Assert\Callback]
    public function validate(ExecutionContextInterface $context): void
    {
        $value = $this->value;
        if (!self::RULES['required'] && ($value === null || (is_string($value) && trim($value) === ''))) { return; }
        if (is_string($value) && self::RULES['type'] !== 'date') { $value = trim($value); }
        $constraints = new Assert\Sequentially([
            new Assert\Type(type: 'string', message: 'Ожидается строка.'),
            new Assert\NotBlank(message: 'Заполните поле.'),
            new Assert\Length(min: 5, max: 180, minMessage: 'Нужно не менее {{ limit }} символов.', maxMessage: 'Допустимо не более {{ limit }} символов.'),
            new Assert\Email(message: 'Укажите email правильного формата.', mode: 'html5')
        ]);
        $context->getValidator()->inContext($context)->atPath('value')->validate($value, $constraints);
    }
}

// Request JSON supplies only the value. It cannot replace the saved rules.
$value = 'demo@example.test';
if (isset($argv[1])) {
    try {
        $raw = file_get_contents($argv[1], false, null, 0, 32769);
        if ($raw === false || strlen($raw) > 32768) { throw new RuntimeException('Invalid file size.'); }
        $input = json_decode($raw, false, 16, JSON_THROW_ON_ERROR);
        if (!$input instanceof stdClass) { throw new RuntimeException('Object required.'); }
        $payload = property_exists($input, 'scenario') ? ($input->data ?? null) : $input;
        if (!$payload instanceof stdClass || !property_exists($payload, 'value')) { throw new RuntimeException('value required.'); }
        $value = $payload->value;
    } catch (Throwable) {
        fwrite(STDERR, "Передайте JSON-объект {\"value\": ...} или запрос {\"scenario\":\"lab\",\"data\":{\"value\":...}} до 32 КБ.\n");
        exit(2);
    }
}
$validator = Validation::createValidatorBuilder()->enableAttributeMapping()->getValidator();
$errors = $validator->validate(new ConfiguredValue($value));
$violations = [];
foreach ($errors as $error) {
    $violations[] = ['path' => $error->getPropertyPath(), 'message' => $error->getMessage(), 'code' => $error->getCode()];
}
echo json_encode(['valid' => count($errors) === 0, 'violations' => $violations], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR), PHP_EOL;
exit(count($errors) === 0 ? 0 : 1);
