Validación PHP / expresiones regulares para URL


125

He estado buscando una expresión regular simple para las URL, ¿alguien tiene una práctica que funcione bien? No encontré uno con las clases de validación de Zend Framework y he visto varias implementaciones.


1
Este es un recurso bastante bueno. Da una lista de muchos patrones y pruebas diferentes: mathiasbynens.be/demo/url-regex
omar j

Respuestas:


79

Utilicé esto en algunos proyectos, no creo que haya tenido problemas, pero estoy seguro de que no es exhaustivo:

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

La mayor parte de la basura aleatoria al final es para lidiar con situaciones como http://domain.com.en una oración (para evitar coincidir con el período final). Estoy seguro de que podría limpiarse, pero ya que funcionó. Lo he copiado más o menos de un proyecto a otro.


77
Algunas cosas que me llaman la atención: el uso de alternancia donde se requieren clases de caracteres (cada alternativa coincide exactamente con un carácter); y el reemplazo no debería haber necesitado las comillas dobles externas (solo fueron necesarias debido al modificador / e inútil en la expresión regular).
Alan Moore

1
@John Scipione: google.comes solo una ruta URL relativa válida pero no una URL absoluta válida. Y creo que eso es lo que está buscando.
Gumbo

Esto no funciona en este caso - que incluye el arrastre ": 3 Cantari noi en albumul <a href=" audio.resursecrestine.ro/cantece/index-autori/andrei-rosu/...>
Softy

1
@Softy algo así http://example.com/somedir/...es una URL perfectamente legítima, que solicita el archivo llamado ..., que es un nombre de archivo legítimo.
Stephen P

Estoy usando Zend \ Validator \ Regex para validar la URL usando su patrón, pero todavía detecta http://www.exampleque es válido
Joko Wandiro

207

Use la filter_var()función para validar si una cadena es URL o no:

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

Es una mala práctica usar expresiones regulares cuando no es necesario.

EDITAR : Tenga cuidado, esta solución no es segura para Unicode y no es segura para XSS. Si necesita una validación compleja, tal vez sea mejor buscar en otro lugar.


29
Hay un error en 5.2.13 (y creo que 5.3.2) que evita que las URL con guiones en ellas se validen con este método.
vamin

14
filter_var rechazará test-site.com , tengo nombres de dominio con guiones, sean válidos o no. No creo que filter_var sea la mejor manera de validar una url. Permitirá una URL comohttp://www
Cesar

44
> Permitirá una URL como ' www ' Está bien cuando la URL como ' localhost '
Stanislav

12
El otro problema con este método es que no es seguro para Unicode.
Benji XVI,

3
FILTER_VALIDATE_URL tiene muchos problemas que deben corregirse. Además, los documentos que describen las banderas no reflejan el código fuente real donde las referencias a algunas banderas se han eliminado por completo. Más información aquí: news.php.net/php.internals/99018
S. Imp

29

Según el manual de PHP, parse_url no debe usarse para validar una URL.

Desafortunadamente, parece que filter_var('example.com', FILTER_VALIDATE_URL)no funciona mejor.

Ambos parse_url()y filter_var()pasarán URL con formato incorrecto comohttp://...

Por lo tanto, en este caso, regex es el mejor método.


10
Este argumento no sigue. Si FILTER_VALIDATE_URL es un poco más permisivo de lo que desea, agregue algunas verificaciones adicionales para tratar esos casos límite. Reinventar la rueda con tu propio intento de regex contra las URL solo te llevará más allá de una verificación completa.
Kzqai

2
Vea todas las expresiones regulares derribadas en esta página para ver ejemplos de por qué -no- escribir las suyas propias.
Kzqai

3
Haces un punto justo Tchalvak. Las expresiones regulares para algo como las URL pueden (según otras respuestas) ser muy difíciles de corregir. Regex no siempre es la respuesta. Por el contrario, la expresión regular tampoco es siempre la respuesta incorrecta. El punto importante es elegir la herramienta correcta (regex o no) para el trabajo y no ser específicamente "anti" o "pro" regex. En retrospectiva, su respuesta de usar filter_var en combinación con restricciones en sus casos límite, parece la mejor respuesta (particularmente cuando las respuestas de expresiones regulares comienzan a llegar a más de 100 caracteres, lo que hace que el mantenimiento de dichas expresiones sea una pesadilla)
catchdave

12

En caso de que quieras saber si la 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);
}

1
Todavía haría algún tipo de validación $urlantes de verificar que la url es real porque la operación anterior es costosa, tal vez hasta 200 milisegundos dependiendo del tamaño del archivo. En algunos casos, la URL puede no tener un recurso disponible en su ubicación todavía (por ejemplo, crear una URL para una imagen que aún no se ha cargado). Además, no está utilizando una versión en caché, por lo que no es así file_exists()que almacenará en caché una estadística en un archivo y volverá casi instantáneamente. Sin embargo, la solución que proporcionó sigue siendo útil. ¿Por qué no solo usar fopen($url, 'r')?
Yzmir Ramirez

Gracias, justo lo que estaba buscando. Sin embargo, cometí un error al intentar usarlo. La función es "url_exist" no "url_exists" oops ;-)
PJ Brunet

9
¿Existe algún riesgo de seguridad al acceder directamente a la URL ingresada por el usuario?
siliconpi

le gustaría agregar un cheque si se encontró un 404: <código> $ httpCode = curl_getinfo ($ c, CURLINFO_HTTP_CODE); // echo $ url. ''. $ httpCode. '<br>'; if ($ httpCode == 404) {echo $ url. ' 404 '; } </code>
Camaleo

No es seguro en absoluto ... se accedería activamente a cualquier URL de entrada.
dmmd

11

Según John Gruber (Daring Fireball):

Expresión regular:

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

utilizando en 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)

Aquí está el patrón extendido de expresiones regulares (con comentarios):

(?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 obtener más detalles, consulte: http://daringfireball.net/2010/07/improved_regex_for_matching_urls


9

No creo que usar expresiones regulares sea algo inteligente en este caso. Es imposible igualar todas las posibilidades e incluso si lo hiciera, todavía existe la posibilidad de que la url simplemente no exista.

Aquí hay una manera muy simple de probar si la URL realmente existe y es legible:

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

(si no existe preg_match, esto también validaría todos los nombres de archivo en su servidor)


7

He usado este con mucho éxito, no recuerdo de dónde lo obtuve

$pattern = "/\b(?:(?:https?|ftp):\/\/|www\.)[-a-z0-9+&@#\/%?=~_|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i";

^ (http: // | https: //)? (([[a-z0-9]? ([-a-z0-9] * [a-z0-9] +)?) {1,63} \ .) + [az] {2,6} (puede ser demasiado codicioso, aún no estoy seguro, pero es más flexible en cuanto a protocolo y www)
andrewbadera

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;
      }
    }

No funciona con enlaces como: 'www.w3schools.com/home/3/?a=l'
usuario3396065

5

Y ahí está tu respuesta =) ¡Intenta romperlo, no puedes!

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;
}


4

Editar:
Como la incidencia señaló, este código ha sido DEPRECADO con el lanzamiento de PHP 5.3.0 (2009-06-30) y debe usarse en consecuencia.


Solo mis dos centavos, pero he desarrollado esta función y la he estado usando durante un tiempo con éxito. Está bien documentado y separado para que pueda cambiarlo fácilmente.

// 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;
}

1
Eregi se eliminará en PHP 6.0.0. Y los dominios con "öäåø" no se validarán con su función. ¿Probablemente deberías convertir la URL a punycode primero?

@incidencia absolutamente de acuerdo. Escribí esto en marzo y PHP 5.3 solo salió a fines de junio configurando eregi como DEPRECATED. Gracias. Voy a editar y actualizar.
Frankie

Corríjame si me equivoco, pero ¿podemos suponer que los TLD tendrán un mínimo de 2 caracteres y un máximo de 6 caracteres?
Yzmir Ramirez

2
@YzmirRamirez (Todos estos años después ...) Si hubo alguna duda cuando escribió su comentario, ciertamente no existe ahora, con TLD en estos días como .photography
Nick Rice

@NickRice tienes razón ... cuánto cambia la web en 5 años. Ahora no puedo esperar hasta que alguien haga el TLD. Supercalifragilisticexpialidocious
Yzmir 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;
    }

Hola, esta solución es buena, y la voté, pero no tiene en cuenta el puerto estándar para https: - sugiero que simplemente reemplace 80 con '' donde funciona el puerto
pgee70

Terminé implementando una variación de esto, porque a mi dominio le importa si una URL existe o no :)
Raz0rwire

2

Inspirado en esta pregunta de .NET StackOverflow y en este artículo referenciado de esa pregunta, existe este validador de URI (URI significa que valida tanto la URL como la 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." );
}

He probado con éxito esta función dentro de un ValueObject que nombré Uriy probé UriTest.

UriTest.php (contiene casos válidos e inválidos para URL y URN)

<?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." );
        }
    }
}

Ejecución de pruebas de unidad

Hay 65 afirmaciones en 46 pruebas. Precaución: hay 2 proveedores de datos para expresiones válidas y 2 más para expresiones no válidas. Uno es para URL y el otro para URN. Si está utilizando una versión de PhpUnit de v5.6 * o anterior, entonces necesita unir los dos proveedores de datos en uno solo.

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

Hay un 100% de cobertura de código en este verificador de URI de muestra.


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

  2. ([a-z0-9 -] +.) + => 2.0 [a-z0-9-] significa cualquier carácter az o cualquier signo 0-9 o (-))

                 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-font>+.)+[azfont>{2,4}(.[azfont>{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: Perdón por el mal inglés. Mi país no lo usa bien.


44
¿Notaste cuántos años tiene esta pregunta? Explique su expresión regular, los usuarios que no lo saben ya tendrán dificultades para comprenderlo sin detalles.
Nic3500

1

Bien, esto es un poco más complejo que una simple expresión regular, pero permite diferentes tipos de URL.

Ejemplos:

Todo lo cual debe 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;
}

Tenga en cuenta que hay una verificación en matriz para los protocolos que desea permitir (actualmente solo http y https están en esa 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

Emite: ErrorException: Índice indefinido: esquema si no se especifica el protocolo, sugiero verificar si está configurado antes.
user3396065

@ user3396065, ¿puede proporcionar una entrada de ejemplo que arroje esto?
Tim Groeneveld

1

La mejor URL Regex que funcionó para mí:

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);
}

Ejemplos:

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

Fuente: http://urlregex.com/


0

Peter Regex no me parece correcto por muchas razones. Permite todo tipo de caracteres especiales en el nombre de dominio y no prueba mucho.

La función de Frankie me parece buena y puedes construir una buena expresión regular de los componentes si no quieres una función, así:

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

No probado pero creo que debería funcionar.

Además, la respuesta de Owen tampoco parece 100%. Tomé la parte de dominio de la expresión regular y la probé en una herramienta de prueba Regex http://erik.eae.net/playground/regexp/regexp.html

Puse la siguiente línea:

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

en la sección "regexp" y la siguiente línea:

-hello.com

en la sección "texto de muestra".

El resultado permitió el carácter negativo a través. Porque \ S significa cualquier carácter no espacial.

Tenga en cuenta que la expresión regular de Frankie maneja el signo menos porque tiene esta parte para el primer personaje:

[a-z0-9]

Lo que no permitirá el signo menos ni ningún otro carácter especial.


0

Aquí está la forma en que lo hice. Pero quiero decir que no soy tan seguro acerca de la expresión regular. Pero debería funcionar tú :)

$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);

De esta manera no necesitará el marcador eval en su patrón.

Espero eso ayude :)


0

Aquí hay una clase simple para la Validación de URL usando RegEx y luego hace referencias cruzadas del dominio contra servidores RBL (Listas de agujeros negros en tiempo real) populares:

Instalar en pc:

require 'URLValidation.php';

Uso:

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

Agregue una URL como parámetro del domain()método y verifique la devolución.

$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>';

}

Salida:

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 puede ver arriba, www.bokranzr.com aparece como sitio web malicioso a través de un RBL, por lo que el dominio se devolvió como falso.



-1

He encontrado que esto es lo más útil para hacer coincidir una URL.

^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$

1
¿Eso coincidirá con las URL que comienzan con ftp:?
andrewsi

/^(https?:\/\/)?([\da-z\font>font>+)\.([az\.font>{2,6})([\/\w \ .-] *) * \ /? $ /
Shahbaz

-1

Hay una función nativa de PHP para eso:

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

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

Devuelve los datos filtrados, o FALSE si el filtro falla.

Compruébalo aquí


¡Esta respuesta duplica una de las respuestas de 2008!
sospechosos
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.