1

I'm working on a Symfony 2 project and I'm making a custom constraint to check if an url exist. I checked around and found this:

How can I check if a URL exists via PHP?

The problem is if I try a totally random address like www.flskkhfkhsdf.com, it gives me a warning and it stop my code. Is there an other way to do that?

The warning:

Warning: get_headers(): php_network_getaddresses: getaddrinfo failed: No such host is known.

Here is my code:

<?php

namespace AdminBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

Class ContrainteUrlExistValidator extends ConstraintValidator
{
    public function validate($value, Constraint $constraint)
    {
        $file_headers = get_headers($value);
        if($file_headers[0] == 'HTTP/1.1 404 Not Found') {
            $this->context->buildViolation($constraint->message)
                ->setParameter('%string%', $value)
                ->addViolation();
        }
    }
}
James Jones
  • 3,850
  • 5
  • 25
  • 44
Kévin Duguay
  • 761
  • 3
  • 12
  • 29
  • Do you mean any URL? Such as external (from application) URL? – felipsmartins Jul 03 '15 at 20:48
  • It looks for a server returning a `404` response. If you write a domain name that doesn't exist, no server will reply and you can impossibly get a `404`. You need to check if the domain (host) exists first. Just like the error message says. – Jan Jul 03 '15 at 21:26

3 Answers3

2

I don't know about Symfony-specific solutions, I will give you some core PHP functions.

gethostbyname is what you need. On a valid hostname, it will return the ip address. On a non-existent hostname, it will return the hostname unmodified.

So you can do something like

if (gethostbyname($hostname) == $hostname) {
    $this->context->buildViolation...
}

Of course you have to extract the base hostname from the given URL, but you can do that with parse_url:

$hostname = parse_url($url, PHP_URL_HOST)

And of course you have to validate the URL first, but you can do that with filter_var:

if ( ! filter_var($url, FILTER_VALIDATE_URL)) {
    // URL not valid
}

EDIT: full code

The full code can be more or less like this:

public function validate($value, Constraint $constraint)
{
    if ( ! filter_var($value, FILTER_VALIDATE_URL)) {
        $this->failValidation();
        return;
    }

    $hostname = parse_url($value, PHP_URL_HOST);
    if (empty($hostname)) {
        $this->failValidation();
        return;
    }

    if (gethostbyname($hostname) == $hostname) {
        $this->failValidation();
        return;
    }
}

protected function failValidation($value, Constraint $constraint) 
{
    $this->context->buildViolation($constraint->message)
            ->setParameter('%string%', $value)
            ->addViolation();
}
Francesco Abeni
  • 4,190
  • 1
  • 19
  • 30
0

You can use any HTTP client library like Guzzle or Buzz to access the URL. These libraries will throw an exception if any error occurs.

Use the HTTP method "HEAD" to avoid downloading the whole page.

GromNaN
  • 582
  • 5
  • 7
0

I found a solution that works:

<?php

namespace AdminBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

Class ContrainteUrlExistValidator extends ConstraintValidator
{
    public function validate($url, Constraint $constraint)
    {
        //Vérifie si l'url peut être vide
        if(empty($url)&&$constraint->peutEtreVide)
        {
            return;
        }

        //Pattern pour trouver les url qui commence par http:// ou https://
        $pattern='/^(https?:\/\/)/';

        //Valide l'url et s'assure le preg_match a trouvé un match
        if(filter_var($url, FILTER_VALIDATE_URL)&&!empty(preg_match($pattern, $url, $matches)))
        {
            //Trouve l'host
            $hostname=parse_url($url, PHP_URL_HOST);

            //Tente de trouver l'adresse IP de l'host
            if (gethostbyname($hostname) !== $hostname)
            {
                //Cherche les données de l'entête
                $headers=get_headers($url);

                //Tente de trouver une erreur 404
                if(!strpos($headers[0], '404'))
                {
                    return;
                }
            }
        }

        //Crée une erreur
        $this->context->buildViolation($constraint->message)
                    ->setParameter('%string%', $url)
                    ->addViolation();
    }
}
Kévin Duguay
  • 761
  • 3
  • 12
  • 29