Perl ja CGI-ohjelmointi
- Perl-ohjelmien suorittaminen
- Perl-syntaksi
- Perlin muuttujatyypit
- Ehtolauseet
- Vertailuoperaattorit
- Boolen logiikka
- Muut operaattorit
- Silmukat
- Tiedostojen käsittely
- Merkkijonojen käsittely
- Aliohjelmat ja funktiot
- Säännölliset lausekkeet (regular expressions)
- CGI-ohjelmat
- Evästeet
- CGI-ohjelmien testaus
- Laajempia CGI-esimerkkejä
- tilaukset.cgi
- Dynaaminen sisältö
- Lisätietoja
Perl on ohjelmointikieli jota käytetään paljon Unix-ympäristöissä ja etenkin CGI-ohjelmien toteutukseen. Tässä dokumentissa käydään hyvin lyhyesti esimerkkien avulla läpi Perl-kielen tärkeimmät ominaisuudet WWW-sovellukset-kurssin kannalta. Tarkempaa tietoa kaipaavat voivat lukea Unix ja shell-ohjelmointi-kurssin perl-materiaalin tai perlintron. Tämän dokumentin sisältö pohjautuu hyvin paljolti perlintroon.
Perl-ohjelmien suorittaminen
Kirjoita perl-ohjelman ensimmäiselle riville perl-tulkin osoite:
#!/usr/bin/perl -w -T
-w -T -optiot asettavat päälle erittäin hyödyllisiä syntaksi ja turvallisuustarkistuksia ja edellyttävät mm. muuttujien esittelyn.
Perl-tulkin sijainnin saat selville kirjoittamalla komentoriville:
[tjlahton@karahka ~]$ whereis perl perl: /usr/bin/perl /usr/share/man/man1/perl.1.gz
Sijainti vaihtelee konekohtaisesti. CGI-ohjelmia kirjoitettaessa perl-tulkin sijainti pitää muistaa tarkistaa www-palvelinkoneessa.
Lisäksi kannattaa ohjelman alkuun lisätä use strict;, joka suojaa pahimmilta kirjoitusvihreiltä.
#!/usr/bin/perl -w -T
use strict;
Anna perl-ohjelmalle suoritusoikeus:
chmod u+x hello.cgi
Jos www-palvelin on säädetty suorittamaan CGI-ohjelmat suEXECin avulla niin riittää kun suoritusoikeus on CGI-ohjelman omistajalla. Jos CGI-ohjelmat suorittaa www-palvelinohjelma esim. apache-käyttäjä niin suoritusoikeus täytyy antaa kaikille:
chmod a+x hello.cgi
Suorita perl-ohjelma:
./hello.cgi
Perl-syntaksi
- Lauseet päättyvät puolipisteeseen (;)
print "Moro";
- Kommentit alkavat #-merkillä
#Seuraava rivi tulostaa tervehdyksen print "Moro"; # kommentin ei tarvi olla rivin alussa
- Välilyönnit, rivinvaihdot ja tabulaattorimerkit ovat merkityksettömiä muulla kuin merkkijonojen sisällä
- Muuttujat esitellään seuraavasti:
my $nimi = "Tommi Lahtonen";
Muuttujien nimet alkavat $-merkillä. Esittelyn yhteydessä muuttuja voidaan myös alustaa. -
Merkkijonot sijoitetaan yksinkertaisten tai kaksinkertaisten lainausten
sisään: "" tai ''
print "Moro"; print 'Moro'; my $nimi = "Tommi Lahtonen"; print "Nimeni on $nimi\n"; #tulostaa Nimeni on Tommi Lahtonen ja rivinvaihto print 'Nimeni on $nimi\n"; #tulostaa Nimeni on $nimi\n
Kaksinkertaisten sulkeiden sisällä tulkitaan myös muuttujat ja erikoismerkit: - funktioita kutsuessa ei tarvita lainausmerkkejä parametrien ympärille mutta selkeyden vuoksi niitä on usein hyvä käyttää.
Perlin muuttujatyypit
Perlissä on kolmea eri muotoa olevia muuttujia: numeerisia/merkkijonoja, taulukoita ja assosiatiivisia taulukoita. Perlissä ei ole C/C++/java-kielien tapaan erilaisia tietotyyppejä.
my $nimi = "Tommi Lahtonen"; my $paino = "125.7"; my @opintoviikot = ("3", "3", "4"); my %kurssit = ("itkp101", "3", "itky201", "3", "tjta270", "4"); my %kurssit = ( itkp101 => "3", itky201 => "3", tjta270 => "4" );
Taulukot
Taulukko on lista arvoja.
Perlin taulukoiden indeksointi alkaa nollasta ( 0 ).
Taulukon yksittäiseen alkioon viitataan kuten muuttujaan.
my $p = "2"; my @opintoviikot = ("3", "5", "4"); print $opintoviikot[0]; # tulostaa taulukon ensimmäisen alkion eli 3 print $opintoviikot[1]; # tulostaa taulukon toisen alkion eli 5 print $opintoviikot[2]; # tulostaa taulukon kolmannen alkion eli 4 print $#opintoviikot; # tulostaa taulukon viimeisen alkion paikan eli 2 print $opintoviikot[$p]; # tulostaa taulukon kolmannen alkion eli 4
Taulukko on myös helppoa järjestää aakkosjärjestykseen:
my @taul = ("foo", "bar", "kukkuu", "luuruu", "möh"); my @jarjestetty_taul = sort(@taul);
Taulukoita voi käyttää hieman kuin pinoa:
my @taulu = (1,2,3 );
push(@taulu, 5); #lisää taulukon loppuun uuden arvon 5
my $arvo = pop(@taulu); #palauttaa taulukon viimeisen arvon ja poistaa sen
unshift(@taulu, 10); #lisää taulukon alkuun uuden arvon 10
$arvo = shift(@taulu); #palauttaa taulukon ensimmäisen arvon ja poistaa sen
Assosiatiiviset taulukot
Assosiatiivisessa taulukossa haluttu taulukon alkio löytyy sitä vastaavan avaimen avulla. Avain voi olla esim. numero tai merkkijono.
my %kurssit = ( itkp101 => "3", itky201 => "3", tjta270 => "4" ); $kurssit("itky201") = 15; #sijoitetaan taulukkoon paikkaan 'itky201' arvo 15 my $foo = "tjta270"; $kurssit{$foo} = 33; # sijoitetaan paikkaan 'tjta270' luku 33 print $kurssit{"itky201"}; # tulostaa 15 print $kurssit{"itkp101"}; # tulostaa 3 print $kurssit{"tjta270"}; # tulostaa 33 print keys %kurssit; # tulostaa taulukon avaimet tuntemattomassa järjestyksessä print values %kurssit; # tulostaa taulukon arvot tuntemattomassa järjestyksessä my @avaimet = keys %kurssit; my @arvot = values %kurssit;
Ehtolauseet
Perl tuntee if-else-rakenteen:
if ( ehtolause ) { ... } else { ... }
Vertailuoperaattorit
Perlissä on vertailuoperaattorit erikseen numeroille ja merkkijonoille. Tapauksesta riippuen pitää valita oikea operaattori.
Numerot | Merkkijonot | Selitys |
---|---|---|
== | eq | yhtäsuuri |
!= | ne | erisuuri |
< | lt | pienempi kuin |
> | gt | suurempi kuin |
<= | le | yhtä suuri tai pienempi |
>= | ge | yhtä suuri tai suurempi |
my $paino = "125"; my $nimi = "Tommi Lahtonen"; if ( $paino > 100 ) { print "Olet aika painava\n"; #tämä rivi tulostuu } else { print "Noin kevyet punnitaan neuvolassa\n"; } if ( $nimi eq "tommi lahtonen" ) { #merkkijonojen yhtäsuuruus print "Taidat olla ope\n"; #tämä rivi tulostuu } else { print "Lienet opiskelija\n"; }
Boolen logiikka
Perlissä on käytössä normaalit loogiset operaattorit joko java-tyyppisesti merkittyinä tai sanallisessa muodossa:
&& | and |
|| | or |
! | not |
if ( $nimi eq "tommi lahtonen" and $paino> 100 ) { print "Taitaa olla Tommin paino nousussa"; } if ( $nimi eq "tommi lahtonen" && $paino> 100 ) { print "Taitaa olla Tommin paino nousussa"; }
Muut operaattorit
- +
- yhteen
- -
- vähennys
- *
- kerto
- /
- jako
- =
- Sijoitusoperaattori
- .
- Merkkijonojen yhdistäminen
my $nimi = "tommi lahtonen"; my $lause = "Minun nimeni on " . $nimi . " ja asun Jyväskylässä\n";
Silmukat
Perlistä löytyy useita silmukkarakenteita joista tärkeimmät ovat foreach- ja while-silmukat.
foreach käy läpi kaikki alkiot:
my @taulukko = (1, 2, 3, 4, 5, 6 ); # jokaisella silmukan kierroksella muuttuja $p # sisältää käsiteltävän taulukon arvon foreach my $p ( @taulukko ) { print $p . "\n"; #tulostaa } my %kurssit = ( itkp101 => "3", itky201 => "3", tjta270 => "4" ); #assosiatiivisen taulukon läpikäynti foreach my $p ( keys %kurssit ) { print "Avaimella $p löytyy arvo " . $kurssit{$p} . "\n"; } my @taulukko2 = ("auto", "mopo", "pyora" ); my $luku = 1; my %tuotteet; foreach my $p ( @taulukko2 ) { $tuotteet{$p} = $luku; $luku++; } foreach my $p ( keys %tuotteet ) { print "Tuote $p : " . $tuotteet{$p} . "\n"; }
while-silmukka toimii kuten javassa.
my $count = 0; my @taulukko = (1, 2, 3, 4, 5, 6 ); while ( $count < @taulukko ) { print $taulukko[$count]; $count = $count + 1; } # tai toinen versio: while ( $count < ($#taulukko+1) ) { print $taulukko[$count]; $count = $count + 1; }
Tiedostojen käsittely
Tiedoston avaaminen:
my $INFILE; my $OUTFILE; my $LOGFILE; open($INFILE, "luku.txt") or die "luku.txt ei aukea: $!"; open($OUTFILE, ">kirjoitus.txt") or die "kirjoitus.txt ei aukea: $!"; open($LOGFILE, ">>lisaa.log") or die "lisaa.log ei aukea: $!";
Tiedostosta lukeminen:
my $line = <$INFILE> #luetaan yksi rivi; my @lines = <$INFILE> #luetaan koko tiedosto; while ($line = <$INFILE>) { print "Rivi: $line"; } close $INFILE;
tiedostoon kirjoittaminen:
open($OUTFILE, ">kirjoitus.txt") or die "kirjoitus.txt ei aukea: $!"; print $OUTFILE "Kirjoitetaan tämä teksti tiedostoon";
Merkkijonojen käsittely
Perl on erityisen hyvä kieli merkkijonojen käsittelyyn.
Tyypillisessä tapauksessa merkkijonoja pitää pilkkoa osiin:
my $rivi = 'Tommi Lahtonen:yliopistonopettaja:2746:tommi.j.lahtonen@jyu.fi'; my @kentat = split(/:/, $rivi); #pilkotaan taulukkoon my ($nimi, $nimike, $puhnro, $email) = split(/:/, $rivi); #pilkotaan muuttujiin
Joskus pitää merkkijonon sisältä etsiä toista merkkijonoa ja ottaa merkkijonosta jokin osa:
my $email = 'tommi.j.lahtonen@jyu.fi'; #etsitään merkkijonon '@' paikka my $paikka = index($email, '@'); #otetaan merkkijonosta pala paikasta 0 kohtaan $paikka saakka my $tunnus = substr($email, 0, $paikka); #otetaan merkkijonosta pala kohdasta $paikka+1 alkaen merkkijonon loppuun saakka my $host = substr($email, $paikka+1);
Aliohjelmat ja funktiot
Perlin aliohjelmiin tuodaan parametrit taulukkona:
sub summa {
my ($luku1, $luku2) = @_; #luetaan funktion parametrit muuttujiin
return $luku1 + $luku2;
}
Jos haluaa tuoda aliohjelmalle parametrina taulukoita niin ne täytyy tuoda referenssinä, joka monimutkaistaa asioita jonkun verran...
Säännölliset lausekkeet (regular expressions)
Perlin hienoimpia ominaisuuksia ovat monipuoliset säännölliset lausekkeet joiden avulla voi tehdä lähes mitä tahansa. Näihin voi perehtyä parhaiten lukemalla PERL Regular Expressions (With Snippets) ja regular expressions.
CGI-ohjelmat
Perlillä tehtyjen CGI-ohjelmien perusrakennusaineena on CGI-kirjasto
#!/usr/bin/perl -w -T
use strict;
use CGI;
my $query = new CGI;
# sivun mediatyyppi on pakko tulostaa. www-palvelin
# lisää muut tarvittavat http-otsikot.
# mediatyypin jälkeen pitää tulla yksi tyhjä rivi jolloin
# www-palvelin tietää että otsikoita ei ole tulossa
# enempää
print "Content-type: text/html\n\n";
CGI-kirjaston mukana saa valmiit funktiot joilla pääsee käsiksi lomakkeilta tulleisiin kenttiin. CGI-kirjaston mukana tulevia mahdollisuuksia HTML-koodin tuottamiseen ei ole mikään pakko käyttää.
my @nimet = $query->param(); # antaa listan lomake-elementtien nimistä my $nimi = $query->param('nimi'); # antaa nimi-nimisen kentän arvon #antaa taulukossa kaikkien 'nimi'-nimisten kenttien arvot my @arvot = $query->param('nimi'); # listataan kaikki kentät ja niiden arvot # jos samannimisellä kentällä on useita arvoja niin listataan # vain ensimmäinen arvo foreach my $p ( $query->param() ) { print "Kenttä $p sai arvoksi: " . $query->param($p) . "\n"; } # listataan varmasti kaikki kentät ja niiden arvot foreach my $p ( $query->param() ) { # otetaan arvo/arvot taulukkon my @arvot = $query->param($p); #käydään kaikki taulukon arvot läpi foreach my $u ( @arvot ) { print "Kenttä $p sai arvoksi: " . $u . "\n"; } }
Koko CGI-objektin voi myös tallentaa tiedostoon tai lukea tiedostosta:
my $INFILE; open($INFILE, "lue.txt") or die "lue.txt ei aukea: $!"; my $query = new CGI($INFILE); my $OUTFILE; open($OUTFILE, ">kirjoitus.txt") or die "kirjoitus.txt ei aukea: $!"; $query->save($OUTFILE); #käsitellään useita my $count = 0; #lasketaan montako tallennettu #eof-funktio palauttaa tosi kun tiedosto on loppu while ( !eof($INFILE) ) { $query = new CGI($INFILE); print $count++ . "\n"; }
Funktiot
CGI-kirjastosta löytyy muutama hyödyllinen funktio:
- escapeHTML
- Muuttaa <, > ja &-merkit entiteeteiksi.
escapeHTML("<h1>Kalle & Ville</h2>");
Muita CGI-kirjaston funktioita
Esimerkki CGI-kirjaston funktioista
Urlien koodaamiseen löytyy sopivat funktiot URI::Escape-kirjastosta:
use URI::Escape; print uri_escape("http://tässä.urlissa.on/laittomia merkkejä"); #koodataan print uri_unescape("http://appro.mit.jyu.fi/%20%e4/"); #palautetaan koodaus
Puhdista ylim. whitespace
Seuraava aliohjelma puhdistaa sille tuodusta merkkijonosta alku- ja loppupään välilyönnit, tabulaattorit ja rivinvaihdot.
sub trim
{
my $string = $_[0];
#Käytetään regexpejä merkkijonon siivoamiseen
$string =~ s/\n//g;
$string =~ s/^\s+//;
$string =~ s/\s+$//;
return $string;
}
Evästeet
Evästeitä voi asettaa ja lukea helpoiten CGI-kirjaston evästefunktioilla:
#!/usr/bin/perl -w -T
use strict;
use CGI;
my $q = new CGI;
# name ja value ovat pakollisia kenttiä
# muut kentät ovat vapaavalintaisia
# expires-kenttään ilmoitetaan evästeen vanhemisaika
# esim. +5h = viiden tunnin kuluttua tai +1d = vuorokauden kuluttua
my $cookie = $q->cookie(-name=>'nimi',
-value=>'tommi',
-expires=>'+5h',
-path=>'/foo/bar',
-domain=>'appro.mit.jyu.fi'
);
# tulostaa Content-type-rivin ja evästeen:
print $q->header(-cookie=>$cookie);
#Selaimen palauttamien evästeiden arvoihin pääsee käsiksi samaan tapaan
#kuin lomake-elementteihin:
# pyydetään 'nimi'-nimisen evästeen arvoa
my $nimi = $q->cookie('nimi');
# käydään läpi kaikki evästeet:
foreach my $p ($q->cookie()) {
print "Löytyi $p-niminen eväste jonka arvo on " . $q->cookie($p);
}
Laskuri toteutettuna evästeellä
Lisätietoja artikkelista PERSISTENT CLIENT STATE HTTP COOKIES.
CGI-ohjelmien testaus
CGI-ohjelmia pitää yleensä ensimmäisenä testata komentoriviltä. Komentorivin kautta voi myös simuloida lomakkeelta tulevia tietoja kirjoittamalla lomakekenttien nimet ja niiden arvot komennon parametreiksi:
./foo.cgi nimi=tommi huone=agc431.2 puhrno=2746
Laajempia CGI-esimerkkejä
Seuraavana muutama isompi esimerkki CGI-ohjelmista
tilaukset.cgi
Kopioi lomake ja cgi-ohjelma haluamallesi palvelimelle samaan kansioon ja kokeile.
Dynaaminen sisältö
Kopioi seuraavat tiedostot haluamallesi palvelimelle samaan kansioon ja kokeile.
Käyttäjien kommentit
virhe taulukot kohdassa. 'print $opintoviikot[$p]; # tulostaa taulukon toisen alkion eli 5' pitäisi olla 'print $opintoviikot[$p]; # tulostaa taulukon kolmannen alkion eli 4'
Virhe korjattu.