Validação / regex do PHP para URL

125

Estou procurando um regex simples para URLs, alguém tem um útil que funcione bem? Eu não encontrei um com as classes de validação de estrutura zend e vi várias implementações.

AndreLiem
fonte
1
Este é um recurso muito bom. Fornece uma lista de vários padrões e testes diferentes: mathiasbynens.be/demo/url-regex #
omar j

Respostas:

79

Usei isso em alguns projetos, não acredito que tenha tido problemas, mas tenho certeza de que não é exaustivo:

$text = preg_replace(
  '#((https?|ftp)://(\S*?\.\S*?))([\s)\[\]{},;"\':<]|\.\s|$)#i',
  "'<a href=\"$1\" target=\"_blank\">$3</a>$4'",
  $text
);

A maior parte do lixo aleatório no final é para lidar com situações como http://domain.com.em uma frase (para evitar coincidir com o período à direita). Tenho certeza de que poderia ser limpo, mas desde que funcionou. Copiei mais ou menos de um projeto para outro.

Owen
fonte
7
Algumas coisas que surgem para mim: uso de alternância onde as classes de caracteres são necessárias (toda alternativa corresponde exatamente a um caractere); e a substituição não deveria precisar das aspas duplas externas (elas eram necessárias apenas por causa do modificador inútil / e no regex).
2777 Alan Moore
1
@ John Scipione: google.comé apenas um caminho de URL relativo válido, mas não um URL absoluto válido. E acho que é isso que ele está procurando.
Gumbo
Isso não funciona neste caso - que inclui a fuga ": 3 cantari noi em albumul <a href=" audio.resursecrestine.ro/cantece/index-autori/andrei-rosu/...>
Softy
1
@Softy http://example.com/somedir/...é uma URL perfeitamente legítima, solicitando o arquivo nomeado ...- que é um nome legítimo.
Stephen P
Estou usando Zend \ Validator \ Regex para url validar usando o seu padrão, mas ainda detectar http://www.exampleseja válido
Joko Wandiro
207

Use a filter_var()função para validar se uma sequência é URL ou não:

var_dump(filter_var('example.com', FILTER_VALIDATE_URL));

É uma prática ruim usar expressões regulares quando não for necessário.

EDIT : Tenha cuidado, esta solução não é segura para unicode e XSS. Se você precisar de uma validação complexa, talvez seja melhor procurar outro lugar.

Stanislav
fonte
29
Existe um bug no 5.2.13 (e acho que 5.3.2) que impede que URLs com traços neles sejam validados usando esse método.
vamin
14
filter_var rejeitará test-site.com , tenho nomes de domínio com traços, sejam eles válidos ou não. Eu não acho que filter_var é a melhor maneira de validar um URL. Permitirá um URL comohttp://www
Cesar
4
> Ele permitirá que uma url como ' www ' É OK quando URL como ' localhost '
Stanislav
12
O outro problema com esse método é que ele não é seguro para Unicode.
Benji XVI
3
FILTER_VALIDATE_URL tem muitos problemas que precisam ser corrigidos . Além disso, os documentos que descrevem os sinalizadores não refletem o código-fonte real, onde as referências a alguns sinalizadores foram completamente removidas. Mais informações aqui: news.php.net/php.internals/99018
S. Imp
29

De acordo com o manual do PHP - parse_url não deve ser usado para validar uma URL.

Infelizmente, parece que filter_var('example.com', FILTER_VALIDATE_URL)não tem um desempenho melhor.

Ambos parse_url()e filter_var()transmitirão URLs malformados, comohttp://...

Portanto, neste caso - regex é o melhor método.

catchdave
fonte
10
Este argumento não segue. Se FILTER_VALIDATE_URL for um pouco mais permissivo do que você deseja, faça algumas verificações adicionais para lidar com esses casos extremos. Reinventar a roda com sua própria tentativa de regex contra URLs só vai levar você a uma verificação completa.
Kzqai 19/07
2
Veja todas as expressões regulares abaixo nesta página para exemplos de por que não escrever seus próprios.
Kzqai 19/07
3
Você faz um ponto justo Tchalvak. Regexes para algo como URLs podem (como em outras respostas) ser muito difícil de acertar. Regex nem sempre é a resposta. Por outro lado, regex também nem sempre é a resposta errada. O ponto importante é escolher a ferramenta certa (regex ou não) para o trabalho e não ser especificamente "anti" ou "pro". Em retrospectiva, sua resposta do uso de filter_var em combinação com restrições em seus casos extremos parece a melhor resposta (principalmente quando as respostas regex começam a chegar a mais de 100 caracteres - tornando a manutenção do referido regex um pesadelo)
catchdave
12

Apenas no caso de você querer saber se o URL realmente existe:

function url_exist($url){//se passar a URL existe
    $c=curl_init();
    curl_setopt($c,CURLOPT_URL,$url);
    curl_setopt($c,CURLOPT_HEADER,1);//get the header
    curl_setopt($c,CURLOPT_NOBODY,1);//and *only* get the header
    curl_setopt($c,CURLOPT_RETURNTRANSFER,1);//get the response as a string from curl_exec(), rather than echoing it
    curl_setopt($c,CURLOPT_FRESH_CONNECT,1);//don't use a cached version of the url
    if(!curl_exec($c)){
        //echo $url.' inexists';
        return false;
    }else{
        //echo $url.' exists';
        return true;
    }
    //$httpcode=curl_getinfo($c,CURLINFO_HTTP_CODE);
    //return ($httpcode<400);
}
Roger
fonte
1
Eu ainda faria algum tipo de validação $urlantes de realmente verificar se o URL é real porque a operação acima é cara - talvez até 200 milissegundos, dependendo do tamanho do arquivo. Em alguns casos, o URL pode ainda não ter um recurso disponível no local (por exemplo, criar um URL para uma imagem que ainda não foi carregada). Além disso, você não está usando uma versão em cache, portanto, não é assim file_exists()que irá armazenar em cache uma estatística em um arquivo e retornar quase instantaneamente. A solução que você forneceu ainda é útil. Por que não usar apenas fopen($url, 'r')?
Yzmir Ramirez 6/08/11
Obrigado, exatamente o que eu estava procurando. No entanto, cometi um erro ao tentar usá-lo. A função é "url_exist" não "url_exists" oops ;-)
PJ Brunet
9
Existe algum risco de segurança ao acessar diretamente a URL inserida pelo usuário?
Siliconpi
você gostaria de adicionar uma verificação se um 404 foi encontrado: <code> $ httpCode = curl_getinfo ($ c, CURLINFO_HTTP_CODE); // echo $ url. ''. $ httpCode. '<br>'; if ($ httpCode == 404) {echo $ url. ' 404 '; } </code>
Camaleo
Não é absolutamente seguro .. qualquer URL de entrada seria acessado ativamente.
Dmmd 28/10/19
11

Conforme John Gruber (Daring Fireball):

Regex:

(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”‘’]))

usando em preg_match ():

preg_match("/(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”‘’]))/", $url)

Aqui está o padrão regex estendido (com comentários):

(?xi)
\b
(                       # Capture 1: entire matched URL
  (?:
    https?://               # http or https protocol
    |                       #   or
    www\d{0,3}[.]           # "www.", "www1.", "www2." … "www999."
    |                           #   or
    [a-z0-9.\-]+[.][a-z]{2,4}/  # looks like domain name followed by a slash
  )
  (?:                       # One or more:
    [^\s()<>]+                  # Run of non-space, non-()<>
    |                           #   or
    \(([^\s()<>]+|(\([^\s()<>]+\)))*\)  # balanced parens, up to 2 levels
  )+
  (?:                       # End with:
    \(([^\s()<>]+|(\([^\s()<>]+\)))*\)  # balanced parens, up to 2 levels
    |                               #   or
    [^\s`!()\[\]{};:'".,<>?«»“”‘’]        # not a space or one of these punct chars
  )
)

Para mais detalhes, consulte: http://daringfireball.net/2010/07/improved_regex_for_matching_urls

abhiomkar
fonte
9

Eu não acho que usar expressões regulares seja uma coisa inteligente a se fazer neste caso. É impossível corresponder a todas as possibilidades e, mesmo que você tenha, ainda há uma chance de que o URL simplesmente não exista.

Aqui está uma maneira muito simples de testar se o URL realmente existe e é legível:

if (preg_match("#^https?://.+#", $link) and @fopen($link,"r")) echo "OK";

(se não houver preg_match, isso também validaria todos os nomes de arquivos no seu servidor)

promaty
fonte
7

Eu usei este com bom sucesso - não me lembro de onde consegui

$pattern = "/\b(?:(?:https?|ftp):\/\/|www\.)[-a-z0-9+&@#\/%?=~_|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i";
Peter Bailey
fonte
^ (http: // | https: //)? (([a-z0-9]? ([-a-z0-9] * [a-z0-9] +)?) {1,63} \ .) + [az] {2,6} (pode ser muito ganancioso, ainda não tenho certeza, mas é mais flexível no protocolo e no www principal)
#
7
    function validateURL($URL) {
      $pattern_1 = "/^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\d+))?\/?/i";
      $pattern_2 = "/^(www)((\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\d+))?\/?/i";       
      if(preg_match($pattern_1, $URL) || preg_match($pattern_2, $URL)){
        return true;
      } else{
        return false;
      }
    }
Vikash Kumar
fonte
Não funciona com links como: 'www.w3schools.com/home/3/?a=l'
user3396065
5

E aí está a sua resposta =) Tente quebrar, você não pode !!!

function link_validate_url($text) {
$LINK_DOMAINS = 'aero|arpa|asia|biz|com|cat|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|mobi|local';
  $LINK_ICHARS_DOMAIN = (string) html_entity_decode(implode("", array( // @TODO completing letters ...
    "&#x00E6;", // æ
    "&#x00C6;", // Æ
    "&#x00C0;", // À
    "&#x00E0;", // à
    "&#x00C1;", // Á
    "&#x00E1;", // á
    "&#x00C2;", // Â
    "&#x00E2;", // â
    "&#x00E5;", // å
    "&#x00C5;", // Å
    "&#x00E4;", // ä
    "&#x00C4;", // Ä
    "&#x00C7;", // Ç
    "&#x00E7;", // ç
    "&#x00D0;", // Ð
    "&#x00F0;", // ð
    "&#x00C8;", // È
    "&#x00E8;", // è
    "&#x00C9;", // É
    "&#x00E9;", // é
    "&#x00CA;", // Ê
    "&#x00EA;", // ê
    "&#x00CB;", // Ë
    "&#x00EB;", // ë
    "&#x00CE;", // Î
    "&#x00EE;", // î
    "&#x00CF;", // Ï
    "&#x00EF;", // ï
    "&#x00F8;", // ø
    "&#x00D8;", // Ø
    "&#x00F6;", // ö
    "&#x00D6;", // Ö
    "&#x00D4;", // Ô
    "&#x00F4;", // ô
    "&#x00D5;", // Õ
    "&#x00F5;", // õ
    "&#x0152;", // Œ
    "&#x0153;", // œ
    "&#x00FC;", // ü
    "&#x00DC;", // Ü
    "&#x00D9;", // Ù
    "&#x00F9;", // ù
    "&#x00DB;", // Û
    "&#x00FB;", // û
    "&#x0178;", // Ÿ
    "&#x00FF;", // ÿ 
    "&#x00D1;", // Ñ
    "&#x00F1;", // ñ
    "&#x00FE;", // þ
    "&#x00DE;", // Þ
    "&#x00FD;", // ý
    "&#x00DD;", // Ý
    "&#x00BF;", // ¿
  )), ENT_QUOTES, 'UTF-8');

  $LINK_ICHARS = $LINK_ICHARS_DOMAIN . (string) html_entity_decode(implode("", array(
    "&#x00DF;", // ß
  )), ENT_QUOTES, 'UTF-8');
  $allowed_protocols = array('http', 'https', 'ftp', 'news', 'nntp', 'telnet', 'mailto', 'irc', 'ssh', 'sftp', 'webcal');

  // Starting a parenthesis group with (?: means that it is grouped, but is not captured
  $protocol = '((?:'. implode("|", $allowed_protocols) .'):\/\/)';
  $authentication = "(?:(?:(?:[\w\.\-\+!$&'\(\)*\+,;=" . $LINK_ICHARS . "]|%[0-9a-f]{2})+(?::(?:[\w". $LINK_ICHARS ."\.\-\+%!$&'\(\)*\+,;=]|%[0-9a-f]{2})*)?)?@)";
  $domain = '(?:(?:[a-z0-9' . $LINK_ICHARS_DOMAIN . ']([a-z0-9'. $LINK_ICHARS_DOMAIN . '\-_\[\]])*)(\.(([a-z0-9' . $LINK_ICHARS_DOMAIN . '\-_\[\]])+\.)*('. $LINK_DOMAINS .'|[a-z]{2}))?)';
  $ipv4 = '(?:[0-9]{1,3}(\.[0-9]{1,3}){3})';
  $ipv6 = '(?:[0-9a-fA-F]{1,4}(\:[0-9a-fA-F]{1,4}){7})';
  $port = '(?::([0-9]{1,5}))';

  // Pattern specific to external links.
  $external_pattern = '/^'. $protocol .'?'. $authentication .'?('. $domain .'|'. $ipv4 .'|'. $ipv6 .' |localhost)'. $port .'?';

  // Pattern specific to internal links.
  $internal_pattern = "/^(?:[a-z0-9". $LINK_ICHARS ."_\-+\[\]]+)";
  $internal_pattern_file = "/^(?:[a-z0-9". $LINK_ICHARS ."_\-+\[\]\.]+)$/i";

  $directories = "(?:\/[a-z0-9". $LINK_ICHARS ."_\-\.~+%=&,$'#!():;*@\[\]]*)*";
  // Yes, four backslashes == a single backslash.
  $query = "(?:\/?\?([?a-z0-9". $LINK_ICHARS ."+_|\-\.~\/\\\\%=&,$'():;*@\[\]{} ]*))";
  $anchor = "(?:#[a-z0-9". $LINK_ICHARS ."_\-\.~+%=&,$'():;*@\[\]\/\?]*)";

  // The rest of the path for a standard URL.
  $end = $directories .'?'. $query .'?'. $anchor .'?'.'$/i';

  $message_id = '[^@].*@'. $domain;
  $newsgroup_name = '(?:[0-9a-z+-]*\.)*[0-9a-z+-]*';
  $news_pattern = '/^news:('. $newsgroup_name .'|'. $message_id .')$/i';

  $user = '[a-zA-Z0-9'. $LINK_ICHARS .'_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\'\[\]]+';
  $email_pattern = '/^mailto:'. $user .'@'.'(?:'. $domain .'|'. $ipv4 .'|'. $ipv6 .'|localhost)'. $query .'?$/';

  if (strpos($text, '<front>') === 0) {
    return false;
  }
  if (in_array('mailto', $allowed_protocols) && preg_match($email_pattern, $text)) {
    return false;
  }
  if (in_array('news', $allowed_protocols) && preg_match($news_pattern, $text)) {
    return false;
  }
  if (preg_match($internal_pattern . $end, $text)) {
    return false;
  }
  if (preg_match($external_pattern . $end, $text)) {
    return false;
  }
  if (preg_match($internal_pattern_file, $text)) {
    return false;
  }

  return true;
}
George Milonas
fonte
Existem muito mais domínios de nível superior .
Jeff Puckett
4

Edit:
Como a incidência apontou, este código foi DEPRECADO com o lançamento do PHP 5.3.0 (30-06-2009) e deve ser usado de acordo.


Apenas meus dois centavos, mas desenvolvi essa função e a uso há algum tempo com sucesso. Está bem documentado e separado para que você possa alterá-lo facilmente.

// Checks if string is a URL
// @param string $url
// @return bool
function isURL($url = NULL) {
    if($url==NULL) return false;

    $protocol = '(http://|https://)';
    $allowed = '([a-z0-9]([-a-z0-9]*[a-z0-9]+)?)';

    $regex = "^". $protocol . // must include the protocol
             '(' . $allowed . '{1,63}\.)+'. // 1 or several sub domains with a max of 63 chars
             '[a-z]' . '{2,6}'; // followed by a TLD
    if(eregi($regex, $url)==true) return true;
    else return false;
}
Frankie
fonte
1
O Eregi será removido no PHP 6.0.0. E domínios com "öäåø" não serão validados com sua função. Você provavelmente deve converter o URL em punycode primeiro?
@incidence absolutamente de acordo. Eu escrevi isso em março e o PHP 5.3 foi lançado apenas no final de junho, definindo eregi como DEPRECATED. Obrigado. Vou editar e atualizar.
1001 Frankie
Corrija-me se estiver errado, mas ainda podemos assumir que os TLDs terão no mínimo 2 caracteres e no máximo 6 caracteres?
Yzmir Ramirez 6/08/11
2
@YzmirRamirez (Todos esses anos mais tarde ...) Se havia alguma dúvida quando você escreveu seu comentário certamente não é agora, com TLDs estes dias, como .photography
Nick Rice,
@NickRice, você está correto ... o quanto a web muda em 5 anos. Agora eu não posso esperar até que alguém faz com que o TLD .supercalifragilisticexpialidocious
Ýzmir Ramirez
4
function is_valid_url ($url="") {

        if ($url=="") {
            $url=$this->url;
        }

        $url = @parse_url($url);

        if ( ! $url) {


            return false;
        }

        $url = array_map('trim', $url);
        $url['port'] = (!isset($url['port'])) ? 80 : (int)$url['port'];
        $path = (isset($url['path'])) ? $url['path'] : '';

        if ($path == '') {
            $path = '/';
        }

        $path .= ( isset ( $url['query'] ) ) ? "?$url[query]" : '';



        if ( isset ( $url['host'] ) AND $url['host'] != gethostbyname ( $url['host'] ) ) {
            if ( PHP_VERSION >= 5 ) {
                $headers = get_headers("$url[scheme]://$url[host]:$url[port]$path");
            }
            else {
                $fp = fsockopen($url['host'], $url['port'], $errno, $errstr, 30);

                if ( ! $fp ) {
                    return false;
                }
                fputs($fp, "HEAD $path HTTP/1.1\r\nHost: $url[host]\r\n\r\n");
                $headers = fread ( $fp, 128 );
                fclose ( $fp );
            }
            $headers = ( is_array ( $headers ) ) ? implode ( "\n", $headers ) : $headers;
            return ( bool ) preg_match ( '#^HTTP/.*\s+[(200|301|302)]+\s#i', $headers );
        }

        return false;
    }
jini
fonte
Olá, esta solução é boa e eu a atualizei, mas ela não leva em conta a porta padrão para https: - sugira que você substitua 80 por '' onde ela funciona na porta
pgee70
Eu acabei implementando uma variação sobre isso, porque meus cuidados de domínio se uma URL realmente existe ou não :)
Raz0rwire
2

Inspirado nesta questão do .NET StackOverflow e neste artigo referenciado dessa pergunta, existe este validador de URI (URI significa que valida a URL e a URN).

if( ! preg_match( "/^([a-z][a-z0-9+.-]*):(?:\\/\\/((?:(?=((?:[a-z0-9-._~!$&'()*+,;=:]|%[0-9A-F]{2})*))(\\3)@)?(?=(\\[[0-9A-F:.]{2,}\\]|(?:[a-z0-9-._~!$&'()*+,;=]|%[0-9A-F]{2})*))\\5(?::(?=(\\d*))\\6)?)(\\/(?=((?:[a-z0-9-._~!$&'()*+,;=:@\\/]|%[0-9A-F]{2})*))\\8)?|(\\/?(?!\\/)(?=((?:[a-z0-9-._~!$&'()*+,;=:@\\/]|%[0-9A-F]{2})*))\\10)?)(?:\\?(?=((?:[a-z0-9-._~!$&'()*+,;=:@\\/?]|%[0-9A-F]{2})*))\\11)?(?:#(?=((?:[a-z0-9-._~!$&'()*+,;=:@\\/?]|%[0-9A-F]{2})*))\\12)?$/i", $uri ) )
{
    throw new \RuntimeException( "URI has not a valid format." );
}

Testei com êxito esta função dentro de um ValueObject que criei nomeado Urie testado por UriTest.

UriTest.php (contém casos válidos e inválidos para URLs e URNs)

<?php

declare( strict_types = 1 );

namespace XaviMontero\ThrasherPortage\Tests\Tour;

use XaviMontero\ThrasherPortage\Tour\Uri;

class UriTest extends \PHPUnit_Framework_TestCase
{
    private $sut;

    public function testCreationIsOfProperClassWhenUriIsValid()
    {
        $sut = new Uri( 'http://example.com' );
        $this->assertInstanceOf( 'XaviMontero\\ThrasherPortage\\Tour\\Uri', $sut );
    }

    /**
     * @dataProvider urlIsValidProvider
     * @dataProvider urnIsValidProvider
     */
    public function testGetUriAsStringWhenUriIsValid( string $uri )
    {
        $sut = new Uri( $uri );
        $actual = $sut->getUriAsString();

        $this->assertInternalType( 'string', $actual );
        $this->assertEquals( $uri, $actual );
    }

    public function urlIsValidProvider()
    {
        return
            [
                [ 'http://example-server' ],
                [ 'http://example.com' ],
                [ 'http://example.com/' ],
                [ 'http://subdomain.example.com/path/?parameter1=value1&parameter2=value2' ],
                [ 'random-protocol://example.com' ],
                [ 'http://example.com:80' ],
                [ 'http://example.com?no-path-separator' ],
                [ 'http://example.com/pa%20th/' ],
                [ 'ftp://example.org/resource.txt' ],
                [ 'file://../../../relative/path/needs/protocol/resource.txt' ],
                [ 'http://example.com/#one-fragment' ],
                [ 'http://example.edu:8080#one-fragment' ],
            ];
    }

    public function urnIsValidProvider()
    {
        return
            [
                [ 'urn:isbn:0-486-27557-4' ],
                [ 'urn:example:mammal:monotreme:echidna' ],
                [ 'urn:mpeg:mpeg7:schema:2001' ],
                [ 'urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66' ],
                [ 'rare-urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66' ],
                [ 'urn:FOO:a123,456' ]
            ];
    }

    /**
     * @dataProvider urlIsNotValidProvider
     * @dataProvider urnIsNotValidProvider
     */
    public function testCreationThrowsExceptionWhenUriIsNotValid( string $uri )
    {
        $this->expectException( 'RuntimeException' );
        $this->sut = new Uri( $uri );
    }

    public function urlIsNotValidProvider()
    {
        return
            [
                [ 'only-text' ],
                [ 'http//missing.colon.example.com/path/?parameter1=value1&parameter2=value2' ],
                [ 'missing.protocol.example.com/path/' ],
                [ 'http://example.com\\bad-separator' ],
                [ 'http://example.com|bad-separator' ],
                [ 'ht tp://example.com' ],
                [ 'http://exampl e.com' ],
                [ 'http://example.com/pa th/' ],
                [ '../../../relative/path/needs/protocol/resource.txt' ],
                [ 'http://example.com/#two-fragments#not-allowed' ],
                [ 'http://example.edu:portMustBeANumber#one-fragment' ],
            ];
    }

    public function urnIsNotValidProvider()
    {
        return
            [
                [ 'urn:mpeg:mpeg7:sch ema:2001' ],
                [ 'urn|mpeg:mpeg7:schema:2001' ],
                [ 'urn?mpeg:mpeg7:schema:2001' ],
                [ 'urn%mpeg:mpeg7:schema:2001' ],
                [ 'urn#mpeg:mpeg7:schema:2001' ],
            ];
    }
}

Uri.php (objeto de valor)

<?php

declare( strict_types = 1 );

namespace XaviMontero\ThrasherPortage\Tour;

class Uri
{
    /** @var string */
    private $uri;

    public function __construct( string $uri )
    {
        $this->assertUriIsCorrect( $uri );
        $this->uri = $uri;
    }

    public function getUriAsString()
    {
        return $this->uri;
    }

    private function assertUriIsCorrect( string $uri )
    {
        // /programming/30847/regex-to-validate-uris
        // http://snipplr.com/view/6889/regular-expressions-for-uri-validationparsing/

        if( ! preg_match( "/^([a-z][a-z0-9+.-]*):(?:\\/\\/((?:(?=((?:[a-z0-9-._~!$&'()*+,;=:]|%[0-9A-F]{2})*))(\\3)@)?(?=(\\[[0-9A-F:.]{2,}\\]|(?:[a-z0-9-._~!$&'()*+,;=]|%[0-9A-F]{2})*))\\5(?::(?=(\\d*))\\6)?)(\\/(?=((?:[a-z0-9-._~!$&'()*+,;=:@\\/]|%[0-9A-F]{2})*))\\8)?|(\\/?(?!\\/)(?=((?:[a-z0-9-._~!$&'()*+,;=:@\\/]|%[0-9A-F]{2})*))\\10)?)(?:\\?(?=((?:[a-z0-9-._~!$&'()*+,;=:@\\/?]|%[0-9A-F]{2})*))\\11)?(?:#(?=((?:[a-z0-9-._~!$&'()*+,;=:@\\/?]|%[0-9A-F]{2})*))\\12)?$/i", $uri ) )
        {
            throw new \RuntimeException( "URI has not a valid format." );
        }
    }
}

Running UnitTests

Existem 65 afirmações em 46 testes. Cuidado: existem 2 provedores de dados para expressões válidas e mais 2 para expressões inválidas. Um é para URLs e o outro para URNs. Se você estiver usando uma versão do PhpUnit da v5.6 * ou anterior, será necessário unir os dois provedores de dados em um único.

xavi@bromo:~/custom_www/hello-trip/mutant-migrant$ vendor/bin/phpunit
PHPUnit 5.7.3 by Sebastian Bergmann and contributors.

..............................................                    46 / 46 (100%)

Time: 82 ms, Memory: 4.00MB

OK (46 tests, 65 assertions)

Cobertura de código

Há 100% de cobertura de código neste verificador de URI de amostra.

Xavi Montero
fonte
2
"/(http(s?):\/\/)([a-z0-9\-]+\.)+[a-z]{2,4}(\.[a-z]{2,4})*(\/[^ ]+)*/i"
  1. (http (s?): //) significa http: // ou https: //

  2. ([a-z0-9 -] +.) + => 2.0 [a-z0-9-] significa qualquer caractere az ou qualquer sinal 0-9 ou (-))

                 2.1 (+) means the character can be one or more ex: a1w, 
                     a9-,c559s, f)
    
                 2.2 \. is (.)sign
    
                 2.3. the (+) sign after ([a-z0-9\-]+\.) mean do 2.1,2.2,2.3 
                    at least 1 time 
                  ex: abc.defgh0.ig, aa.b.ced.f.gh. also in case www.yyy.com
    
                 3.[a-z]{2,4} mean a-z at least 2 character but not more than 
                              4 characters for check that there will not be 
                              the case 
                              ex: https://www.google.co.kr.asdsdagfsdfsf
    
                 4.(\.[a-z]{2,4})*(\/[^ ]+)* mean 
    
                   4.1 \.[a-z]{2,4} means like number 3 but start with 
                       (.)sign 
    
                   4.2 * means (\.[a-z]{2,4})can be use or not use never mind
    
                   4.3 \/ means \
                   4.4 [^ ] means any character except blank
                   4.5 (+) means do 4.3,4.4,4.5 at least 1 times
                   4.6 (*) after (\/[^ ]+) mean use 4.3 - 4.5 or not use 
                       no problem
    
                   use for case https://stackoverflow.com/posts/51441301/edit
    
                   5. when you use regex write in "/ /" so it come

    "/(http(s?)://)([a-z0-9-)+.)+[azorgeous{2,4}(.[azorgeous{2,4}) (/ [^] + ) / i "

                   6. almost forgot: letter i on the back mean ignore case of 
                      Big letter or small letter ex: A same as a, SoRRy same 
                      as sorry.

Nota: Desculpe pelo mau inglês. Meu país não usa bem.

Some_North_korea_kid
fonte
4
Você notou quantos anos essa pergunta tem? Por favor, explique seu regex, os usuários que ainda não sabem terão dificuldade em entendê-lo sem detalhes.
Nic3500
1

OK, então isso é um pouco mais complexo que um simples regex, mas permite diferentes tipos de URLs.

Exemplos:

Tudo o que deve ser marcado como válido.

function is_valid_url($url) {
    // First check: is the url just a domain name? (allow a slash at the end)
    $_domain_regex = "|^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(\.[A-Za-z]{2,})/?$|";
    if (preg_match($_domain_regex, $url)) {
        return true;
    }

    // Second: Check if it's a url with a scheme and all
    $_regex = '#^([a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))$#';
    if (preg_match($_regex, $url, $matches)) {
        // pull out the domain name, and make sure that the domain is valid.
        $_parts = parse_url($url);
        if (!in_array($_parts['scheme'], array( 'http', 'https' )))
            return false;

        // Check the domain using the regex, stops domains like "-example.com" passing through
        if (!preg_match($_domain_regex, $_parts['host']))
            return false;

        // This domain looks pretty valid. Only way to check it now is to download it!
        return true;
    }

    return false;
}

Observe que há uma verificação in_array para os protocolos que você deseja permitir (atualmente apenas http e https estão nessa lista).

var_dump(is_valid_url('google.com'));         // true
var_dump(is_valid_url('google.com/'));        // true
var_dump(is_valid_url('http://google.com'));  // true
var_dump(is_valid_url('http://google.com/')); // true
var_dump(is_valid_url('https://google.com')); // true
Tim Groeneveld
fonte
Lança: ErrorException: índice indefinido: esquema se o protocolo não for especificado, sugiro verificar se está definido antes.
user3396065
@ user3396065, você pode fornecer um exemplo de entrada que lança isso?
Tim Groeneveld
1

O melhor URL Regex que funcionou para mim:

function valid_URL($url){
    return preg_match('%^(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@|\d{1,3}(?:\.\d{1,3}){3}|(?:(?:[a-z\d\x{00a1}-\x{ffff}]+-?)*[a-z\d\x{00a1}-\x{ffff}]+)(?:\.(?:[a-z\d\x{00a1}-\x{ffff}]+-?)*[a-z\d\x{00a1}-\x{ffff}]+)*(?:\.[a-z\x{00a1}-\x{ffff}]{2,6}))(?::\d+)?(?:[^\s]*)?$%iu', $url);
}

Exemplos:

valid_URL('https://twitter.com'); // true
valid_URL('http://twitter.com');  // true
valid_URL('http://twitter.co');   // true
valid_URL('http://t.co');         // true
valid_URL('http://twitter.c');    // false
valid_URL('htt://twitter.com');   // false

valid_URL('http://example.com/?a=1&b=2&c=3'); // true
valid_URL('http://127.0.0.1');    // true
valid_URL('');                    // false
valid_URL(1);                     // false

Fonte: http://urlregex.com/

Fred Vanelli
fonte
0

O Regex de Peter não parece correto para mim por muitas razões. Ele permite todos os tipos de caracteres especiais no nome do domínio e não faz muitos testes.

A função de Frankie parece boa para mim e você pode criar uma boa regex a partir dos componentes, se não quiser uma função, da seguinte forma:

^(http://|https://)(([a-z0-9]([-a-z0-9]*[a-z0-9]+)?){1,63}\.)+[a-z]{2,6}

Não testado, mas acho que deve funcionar.

Além disso, a resposta de Owen também não parece 100%. Peguei a parte do domínio do regex e testei-o em uma ferramenta de teste do Regex http://erik.eae.net/playground/regexp/regexp.html

Eu coloquei a seguinte linha:

(\S*?\.\S*?)

na seção "regexp" e na seguinte linha:

-hello.com

na seção "texto de amostra".

O resultado permitiu o caractere de menos. Porque \ S significa qualquer caractere não espacial.

Observe que o regex de Frankie lida com o sinal de menos porque tem essa parte para o primeiro caractere:

[a-z0-9]

O que não permitirá o menos ou qualquer outro caractere especial.

joedevon
fonte
0

Aqui está o jeito que eu fiz. Mas quero dizer que não sou tão tímido quanto ao regex. Mas deve funcionar tu :)

$pattern = "#((http|https)://(\S*?\.\S*?))(\s|\;|\)|\]|\[|\{|\}|,|”|\"|'|:|\<|$|\.\s)#i";
        $text = preg_replace_callback($pattern,function($m){
                return "<a href=\"$m[1]\" target=\"_blank\">$m[1]</a>$m[4]";
            },
            $text);

Dessa forma, você não precisará do marcador de avaliação em seu padrão.

Espero que ajude :)

Thomas Venturini
fonte
0

Aqui está uma classe simples para validação de URL usando RegEx e, em seguida, faça referência cruzada do domínio com os populares servidores RBL (Realtime Blackhole Lists):

Instalar:

require 'URLValidation.php';

Uso:

require 'URLValidation.php';
$urlVal = new UrlValidation(); //Create Object Instance

Adicione uma URL como parâmetro do domain()método e verifique o retorno.

$urlArray = ['http://www.bokranzr.com/test.php?test=foo&test=dfdf', 'https://en-gb.facebook.com', 'https://www.google.com'];
foreach ($urlArray as $k=>$v) {

    echo var_dump($urlVal->domain($v)) . ' URL: ' . $v . '<br>';

}

Resultado:

bool(false) URL: http://www.bokranzr.com/test.php?test=foo&test=dfdf
bool(true) URL: https://en-gb.facebook.com
bool(true) URL: https://www.google.com

Como você pode ver acima, www.bokranzr.com é listado como site malicioso por meio de uma RBL, de modo que o domínio foi retornado como falso.

Kitson88
fonte
-1

Eu achei que isso era o mais útil para combinar um URL.

^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$
Jeremy Moore
fonte
1
Isso corresponde aos URLs que começam com ftp:?
precisa saber é o seguinte
/^(https?:\/\/)?([\da-z\.-)+)\.([az\.}{2,6})([\/\w \ .-] *) * \ /? $ /
Shahbaz 26/09
-1

Existe uma função nativa do PHP para isso:

$url = 'http://www.yoururl.co.uk/sub1/sub2/?param=1&param2/';

if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) {
    // Wrong
}
else {
    // Valid
}

Retorna os dados filtrados, ou FALSE se o filtro falhar.

Confira aqui

Fredmat
fonte
Esta resposta duplica uma das respostas de 2008!
usar o seguinte