100 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
		
		
			
		
	
	
			100 lines
		
	
	
		
			5.6 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\UnexpectedTypeException;
							 | 
						||
| 
								 | 
							
								use Symfony\Component\Validator\Exception\UnexpectedValueException;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * @author Bernhard Schussek <bschussek@gmail.com>
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								class UrlValidator extends ConstraintValidator
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    public const PATTERN = '~^
							 | 
						||
| 
								 | 
							
								            (%s)://                                 # protocol
							 | 
						||
| 
								 | 
							
								            (((?:[\_\.\pL\pN-]|%%[0-9A-Fa-f]{2})+:)?((?:[\_\.\pL\pN-]|%%[0-9A-Fa-f]{2})+)@)?  # basic auth
							 | 
						||
| 
								 | 
							
								            (
							 | 
						||
| 
								 | 
							
								                (?:
							 | 
						||
| 
								 | 
							
								                    (?:
							 | 
						||
| 
								 | 
							
								                        (?:[\pL\pN\pS\pM\-\_]++\.)+
							 | 
						||
| 
								 | 
							
								                        (?:
							 | 
						||
| 
								 | 
							
								                            (?:xn--[a-z0-9-]++)                       # punycode in tld
							 | 
						||
| 
								 | 
							
								                            |
							 | 
						||
| 
								 | 
							
								                            (?:[\pL\pN\pM]++)                         # no punycode in tld
							 | 
						||
| 
								 | 
							
								                        )
							 | 
						||
| 
								 | 
							
								                    )                                                 # a multi-level domain name
							 | 
						||
| 
								 | 
							
								                        |
							 | 
						||
| 
								 | 
							
								                    [a-z0-9\-\_]++                                    # a single-level domain name
							 | 
						||
| 
								 | 
							
								                )\.?
							 | 
						||
| 
								 | 
							
								                    |                                                 # or
							 | 
						||
| 
								 | 
							
								                \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}                    # an IP address
							 | 
						||
| 
								 | 
							
								                    |                                                 # or
							 | 
						||
| 
								 | 
							
								                \[
							 | 
						||
| 
								 | 
							
								                    (?:(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){6})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:::(?:(?:(?:[0-9a-f]{1,4})):){5})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){4})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,1}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){3})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,2}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){2})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,3}(?:(?:[0-9a-f]{1,4})))?::(?:(?:[0-9a-f]{1,4})):)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,4}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,5}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,6}(?:(?:[0-9a-f]{1,4})))?::))))
							 | 
						||
| 
								 | 
							
								                \]  # an IPv6 address
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            (:[0-9]+)?                              # a port (optional)
							 | 
						||
| 
								 | 
							
								            (?:/ (?:[\pL\pN\pS\pM\-._\~!$&\'()*+,;=:@]|%%[0-9A-Fa-f]{2})* )*    # a path
							 | 
						||
| 
								 | 
							
								            (?:\? (?:[\pL\pN\-._\~!$&\'\[\]()*+,;=:@/?]|%%[0-9A-Fa-f]{2})* )?   # a query (optional)
							 | 
						||
| 
								 | 
							
								            (?:\# (?:[\pL\pN\-._\~!$&\'()*+,;=:@/?]|%%[0-9A-Fa-f]{2})* )?       # a fragment (optional)
							 | 
						||
| 
								 | 
							
								        $~ixuD';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public function validate(mixed $value, Constraint $constraint): void
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if (!$constraint instanceof Url) {
							 | 
						||
| 
								 | 
							
								            throw new UnexpectedTypeException($constraint, Url::class);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (null === $value || '' === $value) {
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (!\is_scalar($value) && !$value instanceof \Stringable) {
							 | 
						||
| 
								 | 
							
								            throw new UnexpectedValueException($value, 'string');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $value = (string) $value;
							 | 
						||
| 
								 | 
							
								        if ('' === $value) {
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (null !== $constraint->normalizer) {
							 | 
						||
| 
								 | 
							
								            $value = ($constraint->normalizer)($value);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $pattern = $constraint->relativeProtocol ? str_replace('(%s):', '(?:(%s):)?', static::PATTERN) : static::PATTERN;
							 | 
						||
| 
								 | 
							
								        $pattern = \sprintf($pattern, implode('|', $constraint->protocols));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (!preg_match($pattern, $value)) {
							 | 
						||
| 
								 | 
							
								            $this->context->buildViolation($constraint->message)
							 | 
						||
| 
								 | 
							
								                ->setParameter('{{ value }}', $this->formatValue($value))
							 | 
						||
| 
								 | 
							
								                ->setCode(Url::INVALID_URL_ERROR)
							 | 
						||
| 
								 | 
							
								                ->addViolation();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if ($constraint->requireTld) {
							 | 
						||
| 
								 | 
							
								            $urlHost = parse_url($value, \PHP_URL_HOST);
							 | 
						||
| 
								 | 
							
								            // the host of URLs with a TLD must include at least a '.' (but it can't be an IP address like '127.0.0.1')
							 | 
						||
| 
								 | 
							
								            if (!str_contains($urlHost, '.') || filter_var($urlHost, \FILTER_VALIDATE_IP)) {
							 | 
						||
| 
								 | 
							
								                $this->context->buildViolation($constraint->tldMessage)
							 | 
						||
| 
								 | 
							
								                    ->setParameter('{{ value }}', $this->formatValue($value))
							 | 
						||
| 
								 | 
							
								                    ->setCode(Url::MISSING_TLD_ERROR)
							 | 
						||
| 
								 | 
							
								                    ->addViolation();
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 |