Delphi i PHP
Transkrypt
Delphi i PHP
Technika Delphi i PHP Komunikacja Pomiędzy Delphi i PHP można stworzyć własne aplikacje do komunikacji pomiędzy tymi językami. Służy do tego metoda POST. Dowiesz się... Powinieneś wiedzieć... • W jaki sposób odebrać dane ze skryptu. • W jaki sposób wysłać dane do skryptu. • Powinieneś znać podstawy języka i środowiska Delphi. • Powinieneś znać podstawy języka PHP i HTML. • Powinieneś znać podstawowe pojęcia związane z protokołem http. Poziom trudności S posób komunikacji, opiera się na metodzie POST, która umożliwia wysyłanie danych przy pomocy nagłówków HTTP. Dane przesyłane tą metodą mają postać: pole1=wartość&pole2=wartość2. Tak więc: nazwa pola jest oddzielona od jego wartości znakiem „=”, natomiast poszczególne porcje danych oddziela znak „&”. Przykładowy nagłówek z powyższymi informacjami wyglądałby więc tak, jak na Listingu 1. Z Delphi do PHP Zacznijmy projektować naszą aplikację, która prześle dane do skryptu metodą POST . Uruchamiamy Delphi i z menu File wybieramy New Application. Po instalacji pakietu INDY w Delphi, przybędzie nam kilka nowych zakładek na palecie komponentów. Odszukajmy zakładkę Indy Clients i wybierzmy z niej komponent IdHTTP. To on umożliwi nam wysyłanie danych. Umieścimy jeszcze następujące komponenty: 3x Edit, 4x Label, 1x Button, oraz 1x Memo (wszystkie one znajdują się w zakładce Standard). Ich przykładowe rozmieszczenie, a także właściwości Caption jakie powin26 niśmy im nadać, zostały zaprezentowane na Rysunku 1. Po umieszczeniu wszystkich elementów, kliknijmy tylko raz na komponent IdHTTP i przejdźmy do Object Inspector’a, a następnie – odszukajmy listę Request. Po jej rozwinięciu ukaże się nam kilka atrybutów, interesować nas będzie ten o nazwie ContentType. W to pole musimy wpisać ciąg: application/x-www-form-urlencoded. Więcej o kodowaniu danych możemy przeczytać w ramce „Kodowanie danych”. Pisanie kodu Projektowanie aplikacji mamy już za sobą, czas więc na napisanie kilku linijek kodu. Nasz program będzie miał za zadanie: • • • • pobrać adres strony; pobrać dwie zmienne do wysłania; wysłać dane; odebrać kod wynikowy. Jego lista zadań jest krótka. Dzięki zastosowaniu komponentu IdHTTP, kod również będzie krótki. Kliknijmy dwa razy na Button’ie i wpiszmy kod, który znajduje się na Listingu 2. Przyjrzyjmy się linijce odpowiadającej za utworzenie strumienia StreamIn, a szczególnie fragmentowi: (Format('text1=%s&text2=%s', [Edit2.Text,Ed it3.Text])). Jak pamiętamy, dane przesyłane metodą POST mają postać: pole1=wartość&pole2=wartość2. Dlatego musimy je przesłać w taki sposób. Nazwy pól: „text1” i „text2” są nazwami przykładowymi, które następnie zastosujemy w skrypcie odbierającym dane. Należy pamiętać, że będą one różne dla każdej strony, która odbiera pakiety z danymi. Powyższa linijka tworzy ciąg powstały z połączenia tekstu w cudzysłowie, z tekstem wpisanym w Edit2 i Edit3. Owy ciąg jest następnie wysyłany do skryptu za pomocą funkcji POST komponentu IdHTTP. Ostatnim krokiem będzie zapisanie i skompilowanie naszego projektu. Gdy mamy już gotową aplikację, możemy stworzyć najprostszy plik PHP, który posłuży nam do jej testowania. Propozycja kodu znajduje się na Listingu 3. W skrypcie, dla ułatwienia, zostały zastosowane zmienne o takich samych nazwach, jak w naszym programie. Po zapisaniu pliku PHP i umieszczeniu go na serwerze, uruchamiamy naszą aplikację, podajemy odpowiednie parametry i naciskamy przycisk POST. Pole tekstowe „Memo” podpisane jako „Kod wynikowy” zostanie automatycznie uzupełnione danymi zwróconymi przez skrypt. Przedstawia to Rysunek 2. Listing 1. Przykładowy nagłówek z danymi wysłanymi metodą POST POST /index.php HTTP/1.1 Host: www.domena.com User-Agent: Mozilla/5.0 Content-Length: 28 Content-Type: application/x-www-formurlencoded pole1=wartość&pole2=wartość2 06/2007 Delphi i PHP Listing 2. Wysyłanie danych z aplikacji do skryptu metodą POST procedure TForm1.Button1Click(Sender: TObject); var StreamIn,StreamOut: TStringStream; begin try {Tworzymy strumień StreamIn zawierający łańcuch danych, zostanie on potem wysłany do skryptu} StreamIn := TStringStream.Create(Format('text1=%s&text2=%s', [Edit2.Text,Edit3.Text] )); {Tworzymy pusty strumień StreamOut. Będzie on odpowiedzialny za odbiór kodu wynikowego skryptu} StreamOut := TStringStream.Create(''); {Procedura POST komponentu IdHTTP i jej kolejne parametry: Adres URL, strumień z danymi, strumień odbierający} IdHTTP1.Post(Edit1.Text,StreamIn,StreamOut); {Wyświetlamy kod wynikowy ze strumienia w Memo} Memo1.Lines.Text:=StreamOut.DataString; finally {Zwalniamy strumienie} StreamIn.Free; StreamOut.Free; end; end; Listing 3. Odbieranie danych z aplikacji wysłanych metodą POST <?php $text1=$_POST['text1']; $text2=$_POST['text2']; echo "Zmienna \"text1\" to: $text1\n"; //wyświetlamy zmienną „text1” echo "Zmienna \"text2\" to: $text2\n"; //wyświetlamy zmienną „text2” ?> Przykładowe zastosowanie Przykładowym zastosowaniem powyższej metody komunikacji może być program, który będzie uploadował wybrane obrazy z naszego dysku twardego na darmowy hosting zdjęć ImageShack (dostępny pod adresem www.imageshack.us). Zaczniemy od małego rekonesansu, tak więc otwieramy ową stronę. Aby uniknąć żmudnej analizy kodu w celu „wydobycia” szczegółów, skorzystajmy z wtyczki do Firefox’a pod nazwą Web Developer (link znajduje się w ramce „W Sieci”). Posiada ona funkcję, która modyfikuje wyświetlaną stronę pokazując, na przykład, szczegóły formularzy. Zostało to pokazane na Rysunku 3. Co na nim widzimy? Po pierwsze – formularz odwołuje się do strony głównej ImageShack.us (fragment form action=””), po drugie- format kodowania danych to multipart/ form-data (fragment enctype=””), i wreszcie po trzecie- pole, z którego pobierana jest ścieżka do wysyłanego pliku (input name="fileupload”). Posiadając te informacje możemy rozpocząć projektowanie aplikacji. Przykładowa aplikacja Zacznijmy od stworzenia nowego projektu. Potrzebne nam będą komponenty: 1x Edit, 1x Memo, 1x button, a także komponent IdHTTP. Podobnie jak ostatnio – kliknijmy raz na komponent IdHTTP i przejdźmy do Object Inspector’a , a następnie – odszukajmy listę Request. Po jej rozwinięciu ukaże nam się kilka atrybutów; interesować nas będzie ten o nazwie ContentType. W to pole musimy wpisać ciąg: multipart/form-data. Rysunek 3. Wtyczka Web Developer wyświetlająca szczegóły formularza na stronie ImageShack.us Teraz, kliknijmy dwa razy na komponent Button, i w edytorze kodu wpiszmy fragment z Listingu 4. Jak widać została tam zawarta zmienna MyData typu TIdMultiPartFormDataStream. Jest to specyficzny typ strumienia Rysunek 1. Przykładowe rozmieszczenie komponentów. Z lewej strony – komponenty ze standardowymi właściwościami Caption, natomiast z prawej – komponenty posiadające zmodyfikowaną tę właściwość Rysunek 2. Gotowa aplikacja wysyłająca dane do skryptu www.phpsolmag.org 27 Technika z danymi, którego implementacja konieczna jest do wysłania danych, zakodowanych w formacie multipart/form-data. Aby z niego skorzystać, konieczne jest dodanie do sekcji Uses modułu IdMultipartFormData. Jest on dołączony do pakietu INDY. Po kompilacji projektu należy w pole Edit wpisać ścieżkę obrazu, który chcemy uploadować, a następnie nacisnąć przycisk Button. Zachęcam do przejrzenia kodów źródłowych, które zostały dołączone do płyty. Do aplikacji dodałem również parser, który pobiera z kodu wynikowego linki do naszego obrazu, ale to temat na inny artykuł. Przykład działania aplikacji pokazany jest na Rysunku 4. Z PHP do Delphi... Teraz nadszedł czas na napisanie programu, który będzie umożliwiał odbieranie zmiennych wysyłanych przez skrypt. Stworzymy nową aplikację i na formie umieścimy komponenty: 2x Label, 1x Edit oraz 2x Memo (wszystkie one znajdują się w zakładce Standard). Ostatnio użyliśmy komponentu, który był klientem protokołu HTTP. Pisząc tę aplikację, będziemy musieli stworzyć serwer HTTP. Odszukajmy więc zakładkę Indy Servers i wybierzmy z niej komponent IdHTTPServer. Ich przykładowe rozmieszczenie, a także właściwości Caption jakie powinniśmy im nadać, zostały zaprezentowane na Rysunku 5. Pisanie kodu Przyszedł czas na oprogramowanie naszej aplikacji. Zacznijmy od aktywacji serwera- klikamy na formie i odszukujemy właściwość OnCreate na zakładce Events w oknie Object Inspector i wpisujemy tam fragment kodu z Listingu 5. podpisanego jako: {Procedura OnCreate}. Deaktywacja serwera odbywać się będzie podczas zamykania programu, więc w zdarzenie Listing 4. Wysyłanie danych z aplikacji do strony ImageShack procedure TForm1.Button1Click(Sender: TObject); var MyData: TIdMultiPartFormDataStream; begin {tworzymy specyficzny strumień z danymi} MyData:=TIdMultiPartFormDataStream.Create; try {kolejny krok to dodanie do strumienia, metodą AddFile, kolejno: nazwy pola (fileupload),ścieżki pliku, a także typu zawartości pakietu z danymi(file)} MyData.AddFile('fileupload',Edit1.Text,'file'); {kod wynikowy zostanie wyświetlony w Memo1} Memo1.Lines.Text:=IdHTTP1.Post('http://imageshack.us/',MyData); finally {Zwalniamy strumień} MyData.Free; end; end; Listing 5. Aktywacja i deaktywacja serwera IdHTTPServer1 {Procedura OnCreate} procedure TForm1.FormCreate(Sender: TObject); OnClose naszej formy, wpiszemy ponownie kod z Listingu 5. który tym razem jest opisany jako: {Procedura OnClose}. Główne zdarzenia zostały już napisane, czas teraz na kod, który umożliwi odbieranie danych. Klikamy na komponent IdHTTPServer1 i przechodzimy do Object Inspector. Odszukujemy Event podpisany jako OnCommandGet i wpisujemy kod z Listingu 6. Aplikacja jest już gotowa, możemy ją zapisać i skompilować. Podobnie jak wcześniej, stworzymy jeszcze plik PHP, z tym, że teraz to on będzie wysyłał dane do serwera. W tym celu posłużymy się najprostszym formularzem, którego propozycja kodu znajduje się na Listingu 7. Pamiętajmy, że nasz program działa na porcie 8008 (aby uniknąć kolizji z innymi aplikacjami), więc będziemy odwoływać się do niego w następujący sposób http://AdresIP:NumerPortu, czyli http:// localhost:8008. Teraz musimy jeszcze uruchomić nasz program i wypełnić pola formularza, a następnie go zatwierdzić. Wynik działania aplikacji, która odebrała parametry i zwróciła wynik do przeglądarki znajduje się na Rysunku 6. Przykładowe zastosowanie Powyższy sposób komunikacji może być wykorzystany- np. do zdalnej administracji komputerem poprzez stronę WWW. Wg definicji serwera aplikacji jest to „program działający na zdalnej maszynie obsługujący żądania kierowane do aplikacji, do której dostęp zapewnia. Użytkownik łączy się za pośrednictwem przeglądarki internetowej, kieruje żądanie do wybranej aplikacji, a całość operacji odbywa się po stronie komputera należącego do organizacji, która udostępnia daną aplikację.”(pl.wikipedia.org).Zastanówmy się najpierw nad ogólną koncepcją naszej aplikacji – zarówno klienta jak i serwera. Klient – w tym przypadku strona WWW – powinien zawierać pola: „Login” oraz „Hasło”, aby naszego zdalnego systemu nie mogły przechwycić begin {Ustawiamy numer portu dla serwera} IdHTTPServer1.DefaultPort := 8008; {aktywujemy serwer} IdHTTPServer1.Active:=True; end; {- - -} {procedura OnClose} procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin {dezaktywujemy serwer} IdHTTPServer1.Active:=False; end; {- - -} 28 Kodowanie danych Jeżeli chcemy wysłać dane do serwera zawierające elementy formularza, to muszą one wcześniej zostać zakodowane. Kodowanie application/x-www-form-urlencoded jest kodowaniem domyślnym, które przeglądarka wysyła do skryptu; używamy go do wysyłania małych ilości informacji. Nie sprawdza się ono jednak podczas wysyłania dużych ilości danych binarnych lub rozległego tekstu. Do tego celu należy użyć kodowania multipart/form-data. Niezależnie od tego, na jakie kodowanie się zdecydujemy, musimy pamiętać o jego implementacji w naszym programie. 06/2007 Delphi i PHP niepowołane osoby, a także kilka opcji wyboru, które będą reprezentowały możliwe do wykonania funkcje przez zdalny system. Po wybraniu opcji „Wyślij”, skrypt wyśle parametry do aplikacji – serwera. Serwer natomiast, po odbiorze porcji danych, powinien mieć możliwość ich walidacji, w tym danych użytkownika (jego nazwy oraz hasła), który chce skorzystać z jego usług, a także oczywiście mieć możliwość wykonania żądanego polecenia. Po jego wykonaniu serwer powinien zwrócić wynik, następnie wszystkie operacje powinny być zapisywane w logach. Listing 6. Odbieranie danych przez aplikację procedure TForm1.IdHTTPServer1CommandGet(AThread: TIdPeerThread; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); var StrResult: String; begin {w Memo zostaną umieszczone żądania (request server)} Memo1.Lines.Add(ARequestInfo.Document); {tworzymy kod wynikowy dla przeglądarki} StrResult := '<h1>Testowa strona serwera.</h1>' +'<b>Odebrane dane:</b>' {ARequestInfo.Host zawiera informacje o hoście użytkownika...} + '<p>Host: ' + ARequestInfo.Host + '</p>' {...natomiast ARequestInfo.UnparsedParams wyświetla parametry przekazane do naszego serwera} + '<p>Parametry: ' + ARequestInfo.UnparsedParams + '</p>'; {wyświetlamy w przeglądarce stworzony łańcuch} AResponseInfo.ContentText := StrResult; {do Edit1 dodajemy parametry przekazane do serwera} Edit1.Text:=ARequestInfo.UnparsedParams; end; Listing 7. Formularz wysyłający zmienne metodą POST do aplikacji <html> <!--implementacja formularza: odwołuje się on do „http://localhost:8008” i przesyła dane metodą „POST”--> <form action="http://localhost:8008" method="post"> <label for="login">Login: </label> <br /> Rysunek 4. Gotowa aplikacja wysyłająca obrazy na hosting ImageShack.us i parsująca kod wynikowy <input name="login" type="text"> <br /> <label for="password">Password: </label> <br /> <input name="password" type="password"> <br /> <input value="GO!" type="submit"> </form> </html> Rysunek 5. Przykładowe rozmieszczenie komponentów. Z góry: komponenty ze standardowymi właściwościami Caption, natomiast u dołu: komponenty posiadające zmodyfikowaną tą właściwość Listing 8. Przykładowa aplikacja klienta <html> <form action="http://localhost:8008" method="post"> <label for="login"><b>Login</b></label> <br /> <input name="login" type="text" value="" style="width: 300px" /> <br /> <label for="pass"><b>Hasło</b></label> <br /> <input name="pass" type="text" value="" style="width: 300px" /> <br /> <label><b>Polecenie</b></label> Rysunek 6. Gotowa aplikacja odbierająca dane od skryptu <br /> <input type="radio" name="opcje" value="shutdown" checked/>Wyłącz zdalny komputer <br /> <input type="radio" name="opcje" value="message"/>Wyślij wiadomość: <br /> <textarea name="wiadomość" style="width: 300px; height: 150px"></textarea> <br /> <input type="submit" name="submit" value="Wyślij"> </form> </html> Rysunek 7. Zarządzanie komputerem z poziomu przeglądarki www.phpsolmag.org 29 Technika Listing 9. Odbieranie danych przez aplikację i wykonywanie żądanych poleceń procedure TForm1.IdHTTPServer1CommandGet(AThread: TIdPeerThread; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); var W Sieci • StrResult: String; • • begin • login,pass,autoryzacja: boolean; http://www.codegear.com/downloads/ free/delphi http://www.indyproject.org/ https://addons.mozilla.org/pl/firefox/ addon/60 http://www.4programmers.net {w Memo zostaną umieszczone żądania (request server)} Memo1.Lines.Add(ARequestInfo.Document); {do logów dodajemy parametry przekazane do serwera} Memo1.Lines.Add('Odebrane parametry:'); Memo1.Lines.Add(ARequestInfo.Params.Text); {walidacja danych:} {czy login (parametr 0) jest poprawny?} if ARequestInfo.Params[0]='login=admin' then login:=True else login:=False; {czy hasło (parametr 1) jest poprawne?} if ARequestInfo.Params[1]='pass=password' then pass:=True else pass:=False; {identyfikacja} if (login and pass) then autoryzacja:=True else autoryzacja:=False; {wypisujemy kod wynikowy} StrResult := '<b>Odebrane dane:</b>' + '<p>Autoryzacja loginu: ' + BoolToStr(login,True) + '</p>' + '<p>Autoryzacja hasła: ' + BoolToStr(pass,True) + '</p>' + '<p>Wykonanie polecenia: ' + BoolToStr(autoryzacja,True) + '</p>'; AResponseInfo.ContentText := StrResult; {zwracamy informację do przeglądarki} AResponseInfo.WriteContent; {sprawdzenie polecenia} if ((ARequestInfo.Params[2]='opcje=message') and autoryzacja) then ShowMessage(StringReplace(ARequestInfo.Params[3],'wiadomość=','',[])) else if ((ARequestInfo.Params[2]='opcje=shutdown') and autoryzacja) then {wywołanie cmd.exe z parametrem shutdown- zamykamy system} ShellExecute(Handle,'open','cmd.exe','/c shutdown /s','C:',SW_HIDE) else end; 30 Projekt klienta Projekt serwera Pierwszym krokiem będzie stworzenie strony WWW, której funkcją będzie wysyłanie pakietów z danymi do naszej aplikacji. Opierać się ona będzie na prostym formularzu, którego kod został zaprezentowany na Listingu 8. Podobnie jak poprzednio, będziemy korzystać z portu numer 8008. Jeżeli zamierzamy korzystać z aplikacji, z innego komputera niż localhost, niekiedy wymagane będzie odblokowanie tego portu na firewall’u. Przyjrzyjmy się Listingowi 8. Jak widać, zostały w nim zawarte planowane pola: Login oraz Hasło, a także pola typu „Radio”, które reprezentują możliwe do wykonania zadania. Dla przykładu umieściłem dwa: możliwość wyłączenia zdalnego komputera, a także opcję wyświetlenia na nim wiadomości. Skoro ten etap mamy już za sobą możemy przejść do projektowania serwera. Projekt serwera w dużej mierze opierać będzie się na aplikacji zaprezentowanej na początku obecnego rozdziału. Śmiało możemy otworzyć tamten projekt i zacząć go modyfikować. Zacznijmy od usunięcia Label-u podpisanego jako „Odebrane parametry”, a także komponentu Edit1. Nie będą one nam już potrzebne. Następnie, tak jak poprzednio, klikamy na komponent IdHTTPServer1 i przechodzimy do Object Inspector’a. Odszukujemy Event podpisany jako OnCommandGet i wpisujemy kod z Listingu 9. Pozostałe procedury projektu pozostawiamy bez zmian. Procedura OnCommandGet, którą przed chwilą wpisaliśmy, zawiera przykładowe polecenie zamykające system- w tym wypadku posłużyliśmy się funkcją ShellExecute, tak więc do sekcji Uses musimy dodać moduł ShellAPI. Sam kod nie jest trudny do zrozumienia, został on oczywiście opatrzony komentarzami, dlatego zachęcam do jego przejrzenia. Przyjrzyjmy się fragmentowi odpowiedzialnemu za walidację danych. Zmienna ARequestInfo.Params[index] zawiera kolejne parametry, do których możemy odwoływać się po ich indeksie. Nasz skrypt, który pisaliśmy wcześniej, wysyła kolejno porcje danych: login, będący parametrem zerowym, hasło, będące parametrem pierwszym, i tak dalej. Ważna jest kolejność, jaką zachowujemy. Wysyłając najpierw hasło, a dopiero potem login do serwera- automatycznie zmieniamy ich indeksy, co powoduje, że dane nie przechodzą walidacji. Możemy oczywiście skorzystać na przykład z instrukcji for...to i sprawdzać po kolei wszystkie odebrane skrypty, jednakże, przy tak małej ilości danych nie jest to konieczne. Wynik działania Proces tworzenia aplikacji, zarówno serwera jak i klienta, mamy już za sobą. Teraz nadszedł czas na przyjrzenie się rezultatom ich działania. Tak więc: uruchamiamy nasze aplikacje- serwer i stronę z formularzem. Wypełniamy podane pola (niekoniecznie poprawnie, aby zobaczyć, czy wszystko działa tak jak należy) i wysyłamy dane. Przykład działania aplikacji pokazany jest na Rysunku 7. Podsumowanie Mój niewielki artykuł opisuje tylko wierzchołek góry lodowej, jaką jest pakiet INDY. Dla jego potrzeb wykorzystałem zaledwie dwa komponenty z ponad ich stuelementowej kolekcji. Pamiętaj, że „teoria śni, praktyka uczy”. W momencie, gdy sam stworzysz i przeanalizujesz aplikacje, zmodyfikujesz ich kody i prześledzisz działanie, będziesz mógł w pełni zrozumieć zagadnienia, które nimi kierują. ARTUR CHUDZIK Autor w wolnych chwilach zajmuje się programowaniem, a także administracją portalem WebHat.pl, którego jest założycielem. Obecnie uczy się w drugiej klasie 1LO w Łańcucie. Kontakt z autorem: [email protected] 06/2007