2011
11.30

Mieliście kiedyś problem ze sprawdzeniem czy adres URL jest poprawny?

Możemy niby napisać jakąś prostą funkcję jeśli wiemy dokładnie co się tam znajdzie. Ale czy będziesz pamiętać o możliwości przekazania parametrów logowania? portu? dostania się poprzez adres IP a nie domenę?

No właśnie…

W PHP mamy niby funkcje parse_url ale w manualu PHP wyraźnie napisane jest:

Ta funkcja nie służy do sprawdzania poprawności podanego URL

Co więc począć?

Z lenistwa i niechęci do studiowania anglojęzycznej dokumentacji zacząłem szukać w internecie idealnego wyrażenia regularnego, które sprawdzi dla mnie poprawność adresu URL. Szperałem i szperałem aż trafiłem tutaj. Mathias włożył sporo trudu w odnalezienie i przetestowanie wielu wyrażeń regularnych. Postanowiłem więc zaufać jego testom (po co robić coś co ktoś już zrobił) i wybrałem najlepsze z wyrażeń regularnych wg. jego testów.

Nie ufałem mu jednak na tyle, żeby w ciemno kopiować co mi w przeglądarce wyskoczyło.

Postarajmy się więc zrozumieć co poniższe wyrażenie może oznaczać:

_^(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)*(?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,})))(?::\d{2,5})?(?:/[^\s]*)?$_iu

Długie jak cholera (502 znaki). Wyczytać stąd nic się nie da, poza tym, że obsługuje tylko protokoły http, https i ftp. Pomogę wam jednak w zrozumieniu tego co się tutaj dzieje.

Nie będę tutaj tłumaczył tego co można znaleźć na Wikipedii. W dodatku po polsku. Odsyłam tutaj.
Po lekturę wyrażeń regularnych w PHP odsyłam z kolei tutaj.
Po przestudiowaniu powyższych KAŻDY pojedynczy element powinien być jasny.

Postarajmy się zrozumieć całość

Rozbijemy to na kawałeczki.

1. Określamy protokoły. Jak pisałem to wyrażenie ogranicza się to http(s) i ftp.

(?:(?:https?|ftp)://)

2. Opcjonalnie login i hasło na serwerze.

(?:\S+(?::\S*)?@)?

Kilka kolejnych wierszy to zdefiniowanie adresu serwera lub domeny. A zaczyna się tak.

(?:

3. Odrzucamy IP sieci lokalnych.

(?!10(?:\.\d{1,3}){3})
(?!127(?:\.\d{1,3}){3})
(?!169\.254(?:\.\d{1,3}){2})
(?!192\.168(?:\.\d{1,3}){2})
(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})

4. Pozwalamy na użycie IP z zakresu sieci globalnej (internetu).

(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])
(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}
(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))

Lub.

|

5. Szukamy domeny.
Najpierw szukamy domeny drugiego rzędu (Second Level Domain, SLD).

(?:(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)

6. Opcjonalnie po kropce pozwalamy na użycie subdomen. Najbardziej zagnieżdżona subdomena w rzeczywistości przechwytywana jest przez wyrażenie widoczne powyżej. Definicja ciągu znaków dla SLD i subdomen jest jednak identyczna. Poniższe wyrażenie definiuje w związku z tym użycie (lub nie :)) subdomen.

(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)*

7. Ostatnim krokiem jest znalezienie domeny najwyższego rzędu (Top Level Domain, TLD).

(?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,}))

Koniec szukania adresu/domeny.

)

8. Następnie port. Opcjonalnie.

(?::\d{2,5})?

9. Również opcjonalnie pozostały ciąg znaków (z wyłączeniem spacji). Ten element akurat da się poprawić i zrobię to w swoim czasie ;)

(?:/[^\s]*)?

Na przykładzie

Adres tego wpisu będzie więc walidowany w następujący sposób.
http://www.paweliwanowski.pl/2011/11/walidacja-adresu-url/

1. http://
5. www
6. .paweliwanowski
7. .pl
9. /2011/11/walidacja-adresu-url/

Reasumując

_^
(?:(?:https?|ftp)://)   # protokół
(?:\S+(?::\S*)?@)?   # login, hasło
(?:
   (?!10(?:\.\d{1,3}){3})  # odrzucone IP
   (?!127(?:\.\d{1,3}){3})
   (?!169\.254(?:\.\d{1,3}){2})
   (?!192\.168(?:\.\d{1,3}){2})
   (?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})

   (?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])  # IP
   (?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}
   (?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))

   |  # lub

   (?:  (?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+) # SLD
   (?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)*   # subdomeny
   (?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,}))   # TLD
)
(?::\d{2,5})?  # port
(?:/[^\s]*)?   # reszta
$_iuS

Podobne artykuły:

Brak komentarzy

Dodaj własny komentarz