124 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			124 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
 | 
						|
/*
 | 
						|
 * This file is part of the Symfony package.
 | 
						|
 *
 | 
						|
 * (c) Fabien Potencier <fabien@symfony.com>
 | 
						|
 *
 | 
						|
 * For the full copyright and license information, please view the LICENSE
 | 
						|
 * file that was distributed with this source code.
 | 
						|
 */
 | 
						|
 | 
						|
namespace Symfony\Component\Validator\Constraints;
 | 
						|
 | 
						|
use Symfony\Component\Validator\Constraint;
 | 
						|
use Symfony\Component\Validator\ConstraintValidator;
 | 
						|
use Symfony\Component\Validator\Exception\LogicException;
 | 
						|
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
 | 
						|
use Symfony\Component\Validator\Exception\UnexpectedValueException;
 | 
						|
 | 
						|
/**
 | 
						|
 * @author Mathieu Lechat <math.lechat@gmail.com>
 | 
						|
 */
 | 
						|
class NoSuspiciousCharactersValidator extends ConstraintValidator
 | 
						|
{
 | 
						|
    private const CHECK_RESTRICTION_LEVEL = 16;
 | 
						|
    private const CHECK_SINGLE_SCRIPT = 16;
 | 
						|
    private const CHECK_CHAR_LIMIT = 64;
 | 
						|
 | 
						|
    private const CHECK_ERROR = [
 | 
						|
        self::CHECK_RESTRICTION_LEVEL => [
 | 
						|
            'code' => NoSuspiciousCharacters::RESTRICTION_LEVEL_ERROR,
 | 
						|
            'messageProperty' => 'restrictionLevelMessage',
 | 
						|
        ],
 | 
						|
        NoSuspiciousCharacters::CHECK_INVISIBLE => [
 | 
						|
            'code' => NoSuspiciousCharacters::INVISIBLE_ERROR,
 | 
						|
            'messageProperty' => 'invisibleMessage',
 | 
						|
        ],
 | 
						|
        self::CHECK_CHAR_LIMIT => [
 | 
						|
            'code' => NoSuspiciousCharacters::RESTRICTION_LEVEL_ERROR,
 | 
						|
            'messageProperty' => 'restrictionLevelMessage',
 | 
						|
        ],
 | 
						|
        NoSuspiciousCharacters::CHECK_MIXED_NUMBERS => [
 | 
						|
            'code' => NoSuspiciousCharacters::MIXED_NUMBERS_ERROR,
 | 
						|
            'messageProperty' => 'mixedNumbersMessage',
 | 
						|
        ],
 | 
						|
        NoSuspiciousCharacters::CHECK_HIDDEN_OVERLAY => [
 | 
						|
            'code' => NoSuspiciousCharacters::HIDDEN_OVERLAY_ERROR,
 | 
						|
            'messageProperty' => 'hiddenOverlayMessage',
 | 
						|
        ],
 | 
						|
    ];
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param string[] $defaultLocales
 | 
						|
     */
 | 
						|
    public function __construct(private readonly array $defaultLocales = [])
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
    public function validate(mixed $value, Constraint $constraint): void
 | 
						|
    {
 | 
						|
        if (!$constraint instanceof NoSuspiciousCharacters) {
 | 
						|
            throw new UnexpectedTypeException($constraint, NoSuspiciousCharacters::class);
 | 
						|
        }
 | 
						|
 | 
						|
        if (null === $value || '' === $value) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        if (!\is_scalar($value) && !$value instanceof \Stringable) {
 | 
						|
            throw new UnexpectedValueException($value, 'string');
 | 
						|
        }
 | 
						|
 | 
						|
        if ('' === $value = (string) $value) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        $checker = new \Spoofchecker();
 | 
						|
        $checks = $constraint->checks;
 | 
						|
 | 
						|
        if (method_exists($checker, 'setRestrictionLevel')) {
 | 
						|
            $checks |= self::CHECK_RESTRICTION_LEVEL;
 | 
						|
            $checker->setRestrictionLevel($constraint->restrictionLevel ?? NoSuspiciousCharacters::RESTRICTION_LEVEL_MODERATE);
 | 
						|
        } elseif (NoSuspiciousCharacters::RESTRICTION_LEVEL_MINIMAL === $constraint->restrictionLevel) {
 | 
						|
            $checks |= self::CHECK_CHAR_LIMIT;
 | 
						|
        } elseif (NoSuspiciousCharacters::RESTRICTION_LEVEL_SINGLE_SCRIPT === $constraint->restrictionLevel) {
 | 
						|
            $checks |= self::CHECK_SINGLE_SCRIPT | self::CHECK_CHAR_LIMIT;
 | 
						|
        } elseif ($constraint->restrictionLevel) {
 | 
						|
            throw new LogicException('You can only use one of RESTRICTION_LEVEL_NONE, RESTRICTION_LEVEL_MINIMAL or RESTRICTION_LEVEL_SINGLE_SCRIPT with intl compiled against ICU < 58.');
 | 
						|
        } else {
 | 
						|
            $checks |= self::CHECK_SINGLE_SCRIPT;
 | 
						|
        }
 | 
						|
 | 
						|
        $checker->setAllowedLocales(implode(',', $constraint->locales ?? $this->defaultLocales));
 | 
						|
 | 
						|
        $checker->setChecks($checks);
 | 
						|
 | 
						|
        if (!$checker->isSuspicious($value, $errorCode)) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        foreach (self::CHECK_ERROR as $check => $error) {
 | 
						|
            if (\PHP_VERSION_ID < 80204) {
 | 
						|
                if (!($checks & $check)) {
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
 | 
						|
                $checker->setChecks($check);
 | 
						|
 | 
						|
                if (!$checker->isSuspicious($value)) {
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
            } elseif (!($errorCode & $check)) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
 | 
						|
            $this->context->buildViolation($constraint->{$error['messageProperty']})
 | 
						|
                ->setParameter('{{ value }}', $this->formatValue($value))
 | 
						|
                ->setCode($error['code'])
 | 
						|
                ->addViolation()
 | 
						|
            ;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |