Zaawansowane przetwarzanie formularzy przy użyciu PHP i JavaScript.
- Dla kogo przeznaczony jest ten artykuł
- Wymagana wiedza
- Wprowadzenie
- Czym jest a czym nie jest JavaScript
- Wykorzystanie JavaScript do zmniejszania ruchu na stronie
- A co jeśli przeglądarka nie obsługuje JavaScript?
- Przekazywanie danych z PHP do JavaScript
- Użycie wyrażeń regularnych
- Wskazówki - przetwarzanie danych
- Wskazówki - projektowanie formularza
- Przykłady kodu
- Podsumowanie
1. Dla kogo przeznaczony jest ten artykuł
Artykuł ten jest przeznaczony dla średniozaawansowanych programistów PHP, którzy wiedzą jak używać formularzy do zbierania informacji, potrafią je przechowywać w bazie danych lub plikach jednorodnych ale nie mają doświadczenia w sprawdzaniu poprawności tych danych.
2. Wymagana wiedza
- Znajomość podstaw działania protokołu HTTP.
- Znajomość podstaw JavaScript.
- Doświadczenie w tworzeniu formularzy HTML.
3. Wprowadzenie
Budując stronę internetową, wcześniej czy później
będziesz potrzebować formularzy, które są najlepszym
sposobem na zbieranie informacji od użytkowników.
Możesz używać prostych formularzy jak np. do logowania
lub rozbudowanych służących np. do rejestracji użytkowników,
w których trzeba wpisać dużo danych. Oczywiście im większy
formularz, tym więcej informacji jest do przetworzenia i tym
większe wyzwanie dla twórcy.
Sprawdzając dane wprowadzone do formularza trzeba pamiętać o
dwóch najistotniejszych kwestiach: złośliwych użytkownikach oraz
zbieraniu możliwie jak największej ilości danych o nich.
Mimo że pojęcia te wydają się sprzeczne ze sobą (z jednej strony
chcesz zbierać jak najwięcej informacji, z drugiej zaś nakładasz
różne ograniczenia z obawy przed złośliwymi użytkownikami), to można
je jednak pogodzić. Wystarczy przedsięwziąć kilka środków ostrożności
aby wyeliminować 95% niebezpieczeństw, jednocześnie zmuszając użytkowników
do wprowadzenia do formularza potrzebnych nam informacji.
Istnieje wiele narzędzi służących walidacji danych przekazywanych za pomocą formularza. Są to
narzędzia działające zarówno po stronie serwera jak i po stronie klienta.
Do najbardziej rozpowszechnionych narzędzi działających po stronie klienta
należą języki skryptowe, a w szczególności JavaScript, która umożliwia
przetwarzanie danych przed wysłaniem ich do serwera.
Po stronie serwera wystarczy PHP. Ale należy pamiętać o tym, że niektóre
pospolite błędy w kodzie PHP mogą mieć fatalne skutki. Więcej szczegółów
na ten temat można znaleźć w artykułach na stronie zend.com, zatytułowanych:
Coding Mistakes Part I, II, III.
4. Czym jest a czym nie jest JavaScript
JavaScript jest bardzo popularnym jezykiem skryptowym działającym po
stronie klienta, używanym do kontrolowania obiektów na stronach www.
Kod JavaScript dołącza się bezpośrednio do kodu HTML. JavaScript przerywa
swoje działanie dopiero w momencie wysłania nowego zapytania do serwera.
Efekty działania skryptu widoczne są do momentu wysłania do serwera - dlatego
jest to język typu client-side (działający po stronie klienta). Fakt, że przy pomocy
JavaScript nie można uzyskać dostępu do systemu plików na maszynie klienta,
sprawia, że jest to język skryptowy a nie programistyczny. JavaScript używany
jest tylko do obsługi obiektów na stronach www.
JavaScript nie jest narzędziem bezpieczeństwa ponieważ kod zostaje
wysłany w całości do klienta. JavaScript używany jest natomiast do
oszczędzania zasobów serwera. Skrypty wykonywane po stronie klienta
i bezpieczeństwo to dwie przeciwności z założenia.
Zasady są skuteczne tak długo jak długo się ich przestrzega. Zasady
JavaScript zostały przygotowane dla użytkowników, którzy nie mają
złych intencji. Ale, jeśli trafi się złośliwy użytkownik, który
bez skrupółów zechce się wedrzeć do twojego systemu, to wykorzysta on
wszelkie luki, o których zapomniałeś w trakcie tworzenia strony.
5. Wykorzystanie JavaScript do zmniejszania ruchu na stronie
JavaScript pozwala na kontrolowanie obiektów HTML na stronie www,
włączając w to formularze. Dzięki JavaScript można zminimalizować
zużycie transferu serwera.
Pomyśl o takiej sytuacji: masz stronę internetową, na której znajduje
się formularz służący do logowania użytkowników, składający się z
dwóch pól: nazwa użytkownika i hasło - żądne z nich nie może być
krótsze niż 6 znaków. Masz dwa wyjścia: albo najpierw sprawdzić
wprowadzane dane przy użyciu JavaScript a potem posłużyć się jeszcze
PHP lub wszystko zrobić przy pomocy PHP. Rozważ następująca sytuację,
w której użytkownik wpisał 5 znaków w pole username.
| JavaScript + PHP
Serwer wysyła do klienta stronę HTML zawierającą kod JavaScript Użytkownik wypełnia formularz JavaScript sprawdza dane i wyświetla ostrzeżenie Klient poprawia dane Klient wysyła ponownie dane do serwera |
PHP
Serwer wysyła strone www do klienta Klient wypełnia formularz Klient wysyła wypełniony formularz do serwera Interpreter PHP sprawdza poprawność danych i znajduje błąd Serwer z powrotem wysyła stronę do klienta z komunikatem o błędzie Klient poprawia błąd Klient wysyła formularz z powrotem do serwera |
Ruch pomiędzy serwerem a klientem nastąpił w czterech miejscach (1, 3, 5, 7) przy użyciu samego PHP i tylko w dwóch miejscach przy użyciu PHP i JavaScript. (patrz przykład kodu nr 4)
Czy obciążenie serwera ma znaczenie? Jeśli PHP pracuje jako CGI to za każdym
wywołaniem skryptu tworzony jest plik wykonywalny co zużywa pamięć oraz zajmuje
moc obliczeniową procesora. Po zakończeniu wykonywania skryptu, plik ten jest
niszczony a zasoby zostają zwolnione. Wykonywanie kilku skryptów PHP jednocześnie
zazwyczaj nie powoduje przeładowania serwera, chyba że istnieje ogromna liczba
uruchomionych jednocześnie instancji PHP. Czas potrzebny do wykonania skryptów PHP
mierzony jest w milisekundach, a zużycie zasobów można obserwować przy użyciu
menedżera zadań w Windows lub przy pomocy komendy ps -ef | grep php
w systemach unixowych.
Wydajność serwera należy wziąść pod uwagę gdy:
- przewiduje się wielu użytkowników na stronie jednocześnie,
- dysponuje się ograniczonymi zasobami (ograniczenia sprzętowe, parametry hostingu itd.)
W przypadku serwerów o średnich parametrach, wydajność nie jest problemem. Dzięki czemu, użycie JavaScript nie ma wpływu na obciążenie serwera. Najważniejszym czynnikiem mającym wpływ na funkcjonowanie strony ma transfer.
6. A co jeśli przeglądarka nie obsługuje JavaScript?
Mimo, że JavaScript nie jest obsługiwany przez każdą przeglądarkę, to jest on
jednak dostępny we wszystkich nowoczesnych aplikacjach. Problem w tym, że można
go łatwo wyłączyć z powodów bezpieczeństwa. Co się stanie gdy JavaScript zostanie
wyłączony lub użytkownik w jakiś sposób oszuka skrypt (np. wysyłając dane pomijając formularz)?
Przed dopuszeczniem do wysłania danych należy zawsze upewnić się czy:
- formularz został wyświetlony i wypełniony
- JavaScript sprawdził wszystkie dane wprowadzone do formularza
Jeżeli przeglądarka nie obsługuje JavaScript to formularz nadal będzie wyświetlany
ale procedury JavaScript nie zadziałają. Aby zabezpieczyć się przed tego typu
sytuacjami można użyć znacznika HTML <noscript>, w którym określa się co ma
być wyświetlone przez przeglądarki nieobsługujące skryptów. Przy pomocy tego znacznika
można wyeliminować zarówno użytkowników korzystających z bardzo starych przeglądarek jak i
tych, którzy celowo wyłączyli obsługę JavaScript.
Następna rzecz, o której należy pamiętać,to żeby nie używać przycisku typu SUBMIT
do wysyłania formularza:
<input type="submit"
value="ok" onClick=
"check_form(this);">
Bo jeżeli tak zrobimy to formularz zostanie wysłany bez względu na to co zwróci JavaScript. Lepiej jest używać obiektu BUTTON z odpowiednią obsługą zdarzeń przypisaną do właściwej funkcji:
<input type="button"
value="ok"
onClick="check_form(this);">
Zapis taki spowoduje wywołanie funkcji sprawdzającej dane i wysyłającej je
do serwera tylko w przypadku gdy nie zawierają one żadnych błędów.
Aby sprawdzić czy formularz został rzeczywiście wypełniony, a nie wysłany przez jakąś
inną aplikację lub ręcznie, można użyć metody obrazkowej. (Zobacz stronę rejestracyjną
na hotmail.com). Na stronie Zend.com znajduje się
tutorial dotyczący tej techniki.
| ! | W walce z wszelkiego rodzaju intruzami twoim najlepszym przyjacielem jest zawsze PHP. Nawet jeśli zastosowałeś JavaScript, nigdy nie pomijaj PHP, który jest najbardziej niezawodnym sposobem sprawdzania danych. |
7. Przekazywanie danych z PHP do JavaScript
Czasami zachodzi potrzeba ponownego użycia danych po stronie klienta. Istnieją dwa sposoby na przekazanie ich z PHP do JavaScript. Pierwszy sposób to wstawienie kodu PHP do znacznika <noscript>:
<?php
$user_id = $_GET["uid"];
?>
<script language="JavaScript1.2">
var user_id=<?php
echo($user_id);?>;
alert("Twój identyfikator to:" + user_id);
</script>
Drugi sposób to wstawienie do formularza pola ukrytego i ustawienie jego wartości przy pomocy PHP. JavaScript może odwoływać się do tej wartości.
<?php
$user_id = $_GET["uid"];
?>
<script language="JavaScript1.2">
// Dzięki id masz bezpośredni dostęp do wartości obiektu
alert("Twój identyfikator to:" + p2j.value);
</script>
<body bgcolor="#ffffff">
<input type="hidden" id=
"p2j" value="
<?php echo ($user_id); ?>
">
8. Użycie wyrażeń regularnych
Bardzo często, przy przetwarzaniu danych z formularza, zachodzi
konieczność wykonywania skomplikowanych operacji na łańcuchach
znaków. Wyrażenia regularne (regexp w skrócie) są potężnym
narzędziem służącym do wyszukiwania części ciągów i ich modyfikacji.
Wyrażenia regularne dostępne są w prawie każdym środowisku, od PHP
po JavaScript i od MySQL po sam UNIX. Wyrażenia te, mimo że różnią się
sposobem implementacji, ogólnie rzecz biorąc są do siebie bardzo podobne
pod względem logiki. Więcej informacji na temat wyrażeń regularnych
można znaleźć w odpowiednich dokumentacjach.
Zazwyczaj, stosowanie wyrażeń regularnych wiąże się z podaniem
większej ilości danych, ale wykonywanie bardzo skomplikowanych
modyfikacji łańcuchów znaków, które normalnie wymagałyby użycia
wielu bloków kodu z pętlami itd., można zapisać w kilku linijkach.
Z drugiej jednak strony, użycie zbyt wielu wyrażeń regularnych
może spowodować nadmierne obciążenie serwera. Dlatego właśnie tak
ważna jest umiejętność rozpoznawania sytuacji, w których należy
ich używać.
W PHP dostępne są dwa rodzaje wyrażeń regularnych: wyrażenia w stylu Perl
i wyrażenia w stylu POSIX 1003.2. Wyrażenia w stylu Perl mają kilka
zalet w porównaniu z wyrażeniami w stylu POSIX.
Tworzenie obiektów regexp w JavaScript podobne jest do tworzenia
definicji zmiennych. Zapisanie zmiennej pomiędzy znakami slash
oznacza, że ma być ona przechowywana jako objekt regexp i można
na niej wykonywać wszelkie operacje dostępne dla tego typu obiektów.
Innym sposobem jest utworzenie instancji klasy theYes. Objekt RegExp.
Składnia wyrażenia regularnego w JavaScript przedstawia się następująco:
regexp.test(string);
Gdzie regexp jest łańcuchem, na którym zostanie wykonana operacja.
Metoda ta sprawdza czy w podanym łańcuchu istnieje określony wzór i zwraca
true jeśli prawda lub false w przeciwnym przypadku (zobacz przykładowy kod 3).
9. Wskazówki - przetwarzanie danych
Sprawdzając dane wprowadzone przez użytkownika, należy pamiętać, że ilość znaków nie jest jedynym kryterium (spacje też są traktowane jako znaki). Aby mieć lepszą kontrole nad wprowadzanymi danymi:
- Wielokrotne spacje zamieniaj zawsze na pojedyncze przy użyciu wyrażeń regularnych. Zasada ta jest szczególnie ważna gdy dane umieszczane są w tablicy przy użyciu funkcji explode, gdzie znakiem rozdzielającym jest właśnie spacja. Wielokrotne spacje spowodują powstanie kluczy z pustymi wartościami (zobacz przykładowy kod 1 i 2).
- Na porównywanych ciągach zawsze używaj funkcji trim przed rozpoczęciem porównywania. W JavaScript nie ma funkcji trim. Zobacz funkcje przedstawione poniżej. (przykładowy kod 5).
- Sprawdzaj typ danych w polach. W PHP dostępne są bardzo użyteczne funkcje do tego służące, np.: is_numeric, is_string, is_integer itd.
-
Cudzysłowy i inne znaki specjalne (procent, podkreślenie, średnik itd.)
mogą powodować problemy z komendami SQL. Należy zawsze używać funkcji PHP
addslashesprzed wysłaniem zapytania SQL, jako że w JavaScript funkcja taka nie jest dostępna. Funkcjaaddslashesdodaje ukośniki ("/") do znaków specjalnych (łącznie z wartością null), dzięki czemu traktowane są one jak zwykłe znaki. W systemach baz danych zarezerwowano nie tylko znaki specjalne ale także niektóre słowa, np.: CREATE, DROP, ALTER, DELETE, GRANT, REVOKE. -
Ogólną zasadą programowania jest, zatrzymywanie wszelkich wyjątków w kodzie,
czyli nie należy dopuszczać do sytuacji, w której użytkownik widzi na monitorze
wiadomość o błędzie. Aby zmylić niedoświadczonych włamywaczy, można zastosować
wyświetlanie informacji przypominających te wyświetlane przez system operacyjny:
$db_conn = mysql_query('localhost', 'u', 'p') or die ( 'ADO Connection Error 0x80000922: Microsoft Jet Engine couldn\'t be started'); // Co do diabła robi tutaj ADO?
$recs = @mysql_query($sql, $db_conn) or die ('ADO Recordset Error 0x800A0BCD: Either BOF or EOF is True, or the current record has been deleted. Requested operation requires a current record.'); -
Jedną z oznak próby włamania jest napływ danych, przy użyciu nieoczekiwanych
metod. Nieoczekiwane zmienne też nie należą do rzeczy przyjemnych. Zamiast
pisać
$zmiennalepiej jest posługiwać się wersją:$_POST['zmienna']lub$_REQUEST['zmienna']. Zawsze należy określać metodę przechowywania danych. Czasami może się zdarzyć, że metod tych jest więcej niż jedna. W takim przypadku należy ustawić je w kolejności:
// Jeśli dostępne są dane z metody POST to należy ich użyć, w przeciwnym wypadku użyj danych z metody GET
$name = isset ($POST['name'] ) ? trim ( $_POST['name'] ) : trim ($_GET[ 'name'] ); -
Jeśli nie zostało zaznaczone jakieś pole to funkcja
issetzwróci wartość false. Dlatego zawsze przed sprawdzeniem wartości pola należy sprawdzić czy zostało ono wogóle wypełnione.
if (isset($_POST['Przeczytaj licencję'])) {
if ($_POST['Przeczytaj licencję'] == "Y") {
echo "Użytkownik zaakceptował postanowienia licencji.";
10. Wskazówki - projektowanie formularza
- Zawsze określaj maksymalną ilość znaków, które można wpisać do pola tekstowego. Dzięki temu unikniesz niespodziewanych błędów.
-
Jeśli chcesz aby wartość pola była wyświetlona bez możliwości jej
zmiany przez użytkownika, stosuj atrybut READONLY oraz ustaw wartość
atrybutu TABINDEX na -1. (Jeśli zastosujesz pole typu HIDDEN to
nie zostanie ono wogóle wyświetlone).
<input type="text" name= "price" readonly tabindex= "-1"> - Jeśli w formularzu znajdzie się kilka pól o tej samej nazwie, to PHP zaakceptuje to pole, które występuje w kodzie jako ostatnie. W przypadku gdy chcesz utworzyć tablicę wartości, umieszczaj nawiasy kwadratowe "[ ]" (bez cudzysłowów) po atrybucie "name" każdego pola. (Zobacz przykładowy kod 6).
11. Przykłady kodu
-
Zamienianie ciągów przy użyciu wyrażeń regularnych w PHP.
<?php
// Zamienianie wielokrotnych spacji na pojedyncze$result = ereg_replace ( "\s+", " ", $_POST ['sentence']);
echo ('Old String:'.$_POST['sentence'] . '\n');
echo ('New String:'.$result. '\n');
?> -
Zamienianie ciągów przy użyciu wyrażeń regularnych w JavaScript.
Uwaga: Pamiętaj, aby po końcowym znaku slash (/) zawsze dodawać "g" (global search), jeśli chcesz zamienić wszystkie pasujące do warunku ciągi. Jeśli tego nie zrobisz, to funkcja<SCRIPT LANGUAGE="JavaScript1.2">
var r, re;
// Ciąg znaków do sprawdzenia
var s= "23th University Summer Olympic Games is going to be organized by city of Izmir in 2005." ;
// Wyrażenie regularne. Maskuje co najmniej jedną spację w zakresie globalnym.
re = /\ s+/g ;
// Zamień wielokrotne spacje na pojedyncze.
r = s.replace(re, " ");
// Wyświetl wyniki
alert("Old string:\n"+s);
alert("New string:\n"+r);
</SCRIPT>replacezamieni tylko pierwsze pasujące wyrażenie. - Dopasowywanie ciągów przy użyciu wyrażeń regularnych w JavaScript.
<SCRIPT LANGUAGE="JavaScript1.2">
// Pierwszy sposób definiowania wyrażeń regularnych
// Widzisz parametry za drugim slashem ?
// i: i g:global search
// To wyrażenie regularne oznacza 5 znaków numerycznych
var re_zip1 = /[0-9]{5}/ig;
// ... i drugi.
var re_zip2 = new RegExp("/[0-9]{5}/", "ig");
// Testowanie. Proszę o uwagę.
alert(re_zip1.test("35140")); // prawda (true). Wszystkie numeryczne i 5 cyfr.
alert(re_zip1.test("3510")); // fałsz. Wszystkie numeryczne ale tylko 4 cyfry.
alert(re_zip2.test("a5140" )); // fałsz. 5 znaków ale nie wszystkie numeryczne.
</SCRIPT> - Typowy kod HTML i JavaScript służący do sprawdzania danych przed wysłaniem.
<HTML>
<HEAD>
<TITLE>Sample Form</TITLE>
</HEAD>
<SCRIPT LANGUAGE="JavaScript1.2">
//Obiekt formularza
var f=document.forms(0);
// Boolen to track if error found
var foundErr;
// Numer elementu formularza, w którym wystąpił pierwszy błąd.
var focusOn;
function check_form() {
foundErr = false; focusOn = -1;
// Pole "Nazwa użytkownika" musi składać się z conajmniej 6 znaków
if (f.user.value.length<6) {
alert ("Za krótka nazwa użytkownika.");
foundErr = true; focusOn = 0;
}
// Hasło musi składać się z co najmniej 6 znaków.
if (f.pass.value.length<6) {
alert("Za krótkie hasło");
foundErr = true;
if (focusOn==-1) focusOn=1;
}
// Czy wystąpił jakiś błąd?
if (foundErr) {
//Tak. Skup się na pierwszym napotkanym.
f.elements.focus(focusOn);
} else {
// Nie. Wyślij formularz.
f.submit();
}
}
</SCRIPT>
<BODY BGCOLOR="#FFFFFF">
<FORM ACTION="check.php" METHOD="post">
<TABLE BORDER="0" WIDTH="100%" CELLSPACING="0" CELLPADDING="0">
< TR>
<TD WIDTH="30%" ALIGN="right">Username</TD>
<TD WIDTH="70%">: <INPUT TYPE="text" NAME="user"></TD>
</TR>
<TR>
<TD ALIGN="right">Password</TD>
<TD>:<INPUT TYPE="password" NAME="pass"></TD>
</TR>
< TR>
<TD> </TD>
<TD><INPUT TYPE="button" VALUE="Submit" onClick="check_form();"></TD>
< /TR>
</TABLE>
</FORM>
</BODY>
</HTML> - Przykład użycia funkcji "trim" w JavaScript.
<SCRIPT LANGUAGE="JavaScript1.2">
function trimmer(pVal) {
TRs=0;
for (i=0; i<pVal.length; i++) {
if (pVal.substr(i,1)==" ") {TRs++;} else {break;}
}
TRe=pVal.length-1;
for (i=TRe; i>TRs-1;i--) {
if (pVal.substr(i,1)==" ") {TRe--;} else {break;}
}
return (pVal.substr(TRs, TRe-TRs+1));
}
</SCRIPT> - Zaisywanie wartości wielu pól typu checkbox do tablicy jeśli
nazwy nie są w nawiasach;
skrypt przetwarzający...
<INPUT TYPE="checkbox" NAME="s" VALUE="A" CHECKED>Checkbox 1<BR>
<INPUT TYPE="checkbox" NAME="s" VALUE="B">Checkbox 2<BR>
<INPUT TYPE="checkbox" NAME="s" VALUE="C">Checkbox 3<BR>
...
zwróci:<?php
echo '<PRE>('.(gettype($_POST['s']).') ';
print_r($_POST['s']);
?>(string) A
Ale jeśli dodasz [] po nazwie każdego elementu,
ten sam kod zwróci:...
<INPUT TYPE="checkbox" NAME="s[]" VALUE="A" CHECKED>Checkbox 1<BR>
<INPUT TYPE="checkbox" NAME="s[]" VALUE="B">Checkbox 2<BR>
<INPUT TYPE="checkbox" NAME="s[]" VALUE="C">Checkbox 3<BR>
...
gdzie widać, że wszystkie trzy pola są zaznaczone.(array)
Array (
[0] => A
[1] => B
[2] => C
)
Podsumowanie
Jednym z najgorszych błędów, jakie można popełnić przy przetwarzaniu danych z formularza jest niesprawdzenie ich poprawności. Błędy dzielą się na dwie kategorie:
- Błędy pospolite: to błędy popełniane w wyniku nieuwagi, bezmyślności lub przeoczenia. Jeśli wiesz, że 2 + 2 = 4, a na egzaminie napiszesz 5 to jest to właśnie błąd pospolity.
- Błędy logiczne: są zazwyczaj rezultatem ignorancji, niedouczenia lub złośliwego działania. Jeśli rzeczywiście myślisz, że 2 + 2 = 5 to popełniasz błąd logiczny.
Powyższa klasyfikacja błędów odnosi się także do wypełniania forumlarzy.
Zwykli odwiedzający zapewne będą popełniać błędy pospolite, które można
wyeliminować przy użyciu JavaScript. Nie należy jednak oczekiwać nic więcej
od JavaScript poza wstępnym przygotowaniem danych do wysłania. Aby obronić
się przed błędami logicznymi, w szczególności powodowanymi przez złośliwych
użytkowników, musisz użyć PHP.
Metody i wskazówki pokazane w tym artykule są tylko wstępem do bardziej
zaawansowanych technik przetwarzania formularzy. Podczas tworzenia
bardziej złożonych formularzy będziesz potrzebować więcej metod sprawdzania
danych. Nie zapomnij podzielić się nimi z innymi!
Autor: Mehmet Avsar
Data: 6.08.2003
Tłumaczenie: Łukasz Piwko
Oryginalna wersja artykułu znajduje się pod adresem
http://www.zend.com/zend/tut/tutorial-mehmet2.php